blob: 5cb1e68765162b67349f00fa29fc99715d4adad6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2014 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.pde.api.tools.generator;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.util.IAttributeNamesConstants;
import org.eclipse.jdt.core.util.IClassFileAttribute;
import org.eclipse.jdt.core.util.IClassFileReader;
import org.eclipse.jdt.core.util.IFieldInfo;
import org.eclipse.jdt.core.util.IInnerClassesAttribute;
import org.eclipse.jdt.core.util.IInnerClassesAttributeEntry;
import org.eclipse.jdt.core.util.IMethodInfo;
import org.eclipse.jdt.core.util.ISignatureAttribute;
import org.eclipse.pde.api.tools.generator.util.Util;
import org.eclipse.pde.api.tools.internal.IApiXmlConstants;
import org.eclipse.pde.api.tools.internal.provisional.ProfileModifiers;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* This is a java application to generate the EE descriptions for specified EE names:
* <p>Accepted names are:</p>
* <ol>
* <li>JRE-1.1,</li>
* <li>J2SE-1.2,</li>
* <li>J2SE-1.3,</li>
* <li>J2SE-1.4,</li>
* <li>J2SE-1.5,</li>
* <li>JavaSE-1.6,</li>
* <li>JavaSE-1.7,</li>
* <li>JavaSE-1.8,</li>
* <li>CDC-1.0_Foundation-1.0,</li>
* <li>CDC-1.1_Foundation-1.1,</li>
* <li>OSGi_Minimum-1.0</li>,
* <li>OSGi_Minimum-1.1,</li>
* <li>OSGi_Minimum-1.2.</li>
* </ol>
* This can be called using:
* -output c:/EE_descriptions -config C:\OSGi_profiles\configuration.properties -EEs JRE-1.1,J2SE-1.2,J2SE-1.3,J2SE-1.4,J2SE-1.5,JavaSE-1.6,JavaSE-1.7,JavaSE-1.8,CDC-1.0_Foundation-1.0,CDC-1.1_Foundation-1.1,OSGi_Minimum-1.0,OSGi_Minimum-1.1,OSGi_Minimum-1.2
*/
public class EEGenerator {
static class AbstractNode {
protected int addedProfileValue = -1;
protected int removedProfileValue = -1;
public void persistAnnotations(Element element) {
if (this.addedProfileValue != -1) {
element.setAttribute(IApiXmlConstants.ATTR_ADDED_PROFILE, Integer.toString(this.addedProfileValue));
}
if (this.removedProfileValue != -1) {
element.setAttribute(IApiXmlConstants.ATTR_REMOVED_PROFILE, Integer.toString(this.removedProfileValue));
}
}
public void persistAnnotations(Element element, String OSGiProfileName) {
int value = ProfileModifiers.getValue(OSGiProfileName);
if (value != -1) {
element.setAttribute(IApiXmlConstants.ATTR_PROFILE, Integer.toString(value));
}
}
public void setAddedProfileValue(int value) {
if (this.addedProfileValue != -1) {
System.err.println("Remove profile value is already set"); //$NON-NLS-1$
}
this.addedProfileValue = value;
}
public void setRemovedProfileValue(int value) {
if (this.removedProfileValue != -1) {
System.err.println("Remove profile value is already set"); //$NON-NLS-1$
}
this.removedProfileValue = value;
}
}
static class Field extends AbstractNode implements Comparable<Field> {
char[] name;
char[] type;
Field(char[] fname, char[] ftype) {
this.name = fname;
if (ftype != null) {
this.type = CharOperation.replaceOnCopy(ftype, '/', '.');
}
}
public int compareTo(Field field) {
return CharOperation.compareTo(this.name, field.name);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Field)) {
return false;
}
Field other = (Field) obj;
return Arrays.equals(name, other.name);
}
public int getStatus() {
return -1;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(name);
result = prime * result + Arrays.hashCode(type);
return result;
}
public void persistXML(Document document, Element parent) {
Element field = document.createElement(IApiXmlConstants.ELEMENT_FIELD);
parent.appendChild(field);
field.setAttribute(IApiXmlConstants.ATTR_NAME, new String(this.name));
field.setAttribute(IApiXmlConstants.ATTR_STATUS, Integer.toString(getStatus()));
}
public void persistXML(Document document, Element parent, String OSGiProfileName) {
Element field = document.createElement(IApiXmlConstants.ELEMENT_FIELD);
parent.appendChild(field);
field.setAttribute(IApiXmlConstants.ATTR_NAME, new String(this.name));
persistAnnotations(field, OSGiProfileName);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder
.append("Field : ") //$NON-NLS-1$
.append(this.name)
.append(' ')
.append(Signature.toCharArray(this.type));
return String.valueOf(builder);
}
}
static class Method extends AbstractNode implements Comparable<Method> {
public static final char[] NO_GENERIC_SIGNATURE = new char[0];
char[] genericSignature;
int modifiers;
char[] selector;
char[] signature;
Method(int mods, char[] select, char[] sig, char[] genericsig) {
this.selector = select;
this.signature = sig;
this.modifiers = mods;
if (genericsig == null) {
this.genericSignature = NO_GENERIC_SIGNATURE;
} else {
this.genericSignature = genericsig;
}
}
public int compareTo(Method method) {
int compare = CharOperation.compareTo(this.selector, method.selector);
if (compare == 0) {
int compareTo = CharOperation.compareTo(this.signature, method.signature);
if (compareTo == 0) {
return this.getStatus() - method.getStatus();
}
return compareTo;
}
return compare;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Method)) {
return false;
}
Method other = (Method) obj;
if (!Arrays.equals(selector, other.selector)) {
return false;
}
return Arrays.equals(signature, other.signature);
}
public int getStatus() {
return -1;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(selector);
result = prime * result + Arrays.hashCode(signature);
return result;
}
public void persistXML(Document document, Element parent) {
Element method = document.createElement(IApiXmlConstants.ELEMENT_METHOD);
parent.appendChild(method);
method.setAttribute(IApiXmlConstants.ATTR_NAME, new String(this.selector));
method.setAttribute(IApiXmlConstants.ATTR_SIGNATURE, new String(this.signature));
method.setAttribute(IApiXmlConstants.ATTR_STATUS, Integer.toString(getStatus()));
}
public void persistXML(Document document, Element parent, String OSGiProfileName) {
Element method = document.createElement(IApiXmlConstants.ELEMENT_METHOD);
parent.appendChild(method);
method.setAttribute(IApiXmlConstants.ATTR_NAME, new String(this.selector));
method.setAttribute(IApiXmlConstants.ATTR_SIGNATURE, new String(this.signature));
persistAnnotations(method, OSGiProfileName);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Method : "); //$NON-NLS-1$
if (this.genericSignature != null && this.genericSignature.length != 0) {
builder.append(Signature.toCharArray(this.genericSignature, this.selector, null, true, true));
} else {
builder.append(Signature.toCharArray(this.signature, this.selector, null, true, true));
}
return String.valueOf(builder);
}
}
static class Package extends AbstractNode implements Comparable<Package> {
String name;
List<Type> types;
public Package(String pname) {
this.name = pname;
}
public void addType(Type type) {
if (this.types == null) {
this.types = new ArrayList<Type>();
}
this.types.add(type);
}
public void collectTypes(Map<String, Type> result) {
Collections.sort(this.types);
for (Iterator<Type> iterator2 = this.types.iterator(); iterator2.hasNext();) {
Type type = iterator2.next();
String typeName = new String(type.name);
result.put(typeName, type);
}
}
public int compareTo(Package package1) {
return this.name.compareTo(package1.name);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Package)) {
return false;
}
Package other = (Package) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
public Type getType(Type typeToFind) {
if (this.types == null) return null;
int index = this.types.indexOf(typeToFind);
if (index != -1) {
return this.types.get(index);
}
return null;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
public void persistAsClassStubsForZip(ZipOutputStream zipOutputStream, ProfileInfo info) throws IOException {
if (this.types == null) return;
Collections.sort(this.types);
for (Iterator<Type> iterator2 = this.types.iterator(); iterator2.hasNext();) {
Type type = iterator2.next();
StringBuffer buffer = new StringBuffer(this.name.replace('.', '/'));
String simpleName = type.getSimpleName();
buffer.append('/').append(simpleName.replace('.', '$'));
byte[] classFileBytes = info.getClassFileBytes(type);
if (classFileBytes != null) {
Util.writeZipFileEntry(zipOutputStream, String.valueOf(buffer), classFileBytes);
}
}
}
public void persistXML(Document document, Element element, String OSGiProfileName) {
Element pkg = document.createElement(IApiXmlConstants.ELEMENT_PACKAGE);
pkg.setAttribute(IApiXmlConstants.ATTR_NAME, this.name);
element.appendChild(pkg);
if (this.types == null) return;
Collections.sort(this.types);
for (Iterator<Type> iterator2 = this.types.iterator(); iterator2.hasNext();) {
iterator2.next().persistXML(document, pkg, OSGiProfileName);
}
}
public int size() {
return this.types == null ? 0 : this.types.size();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Package : "); //$NON-NLS-1$
builder.append(this.name).append(Util.LINE_SEPARATOR);
return String.valueOf(builder);
}
}
static class ProfileInfo {
private static final String BLACK_LIST_NAME = "_blackList_.txt"; //$NON-NLS-1$
private static final String CDC_SUBDIR = "cdc"; //$NON-NLS-1$
private static final String JRE_SUBDIR = "jre"; //$NON-NLS-1$
private static final String OSGI_SUBDIR = "osgi"; //$NON-NLS-1$
private static final String OTHER_PACKAGES = "org.osgi.framework.system.packages"; //$NON-NLS-1$
private static boolean checkDocStatus(ProfileInfo info, Type type, ZipFile docZip, String docURL, String docRoot) {
if (docZip == null && docURL == null) {
// if no doc to validate we accept it if on white list
if (DEBUG) {
System.out.println("No javadoc zip or url for " + info.profileName); //$NON-NLS-1$
}
return info.isOnWhiteList(type);
}
String typeName = getDocTypeName(docRoot, type);
if (DEBUG) {
System.out.println("Retrieving javadoc for type: " + typeName); //$NON-NLS-1$
}
if (docZip == null) {
char[] contents = info.getOnlineDocContents(docURL, typeName);
if (contents == null) {
if (DEBUG) {
System.out.println("Found no doc for " + typeName + " - check whitelist"); //$NON-NLS-1$ //$NON-NLS-2$
}
return info.isOnWhiteList(type);
}
return true;
}
return docZip.getEntry(typeName) != null || info.isOnWhiteList(type);
}
public static String getDocTypeName(String docRoot, Type type) {
StringBuffer buffer = new StringBuffer(docRoot);
char[] typeNameForDoc = CharOperation.replaceOnCopy(type.name, '.', '/');
typeNameForDoc = CharOperation.replaceOnCopy(typeNameForDoc, '$', '.');
buffer.append(typeNameForDoc);
buffer.append(".html"); //$NON-NLS-1$
return String.valueOf(buffer);
}
public static ProfileInfo getProfileInfo(
String profileName,
String jreLib,
String osgiProfile,
String jreDoc,
String jreURL,
String docRoot,
String cacheLocation,
String whiteList) {
ProfileInfo info = new ProfileInfo(profileName, jreLib, osgiProfile, jreDoc, jreURL, docRoot, cacheLocation, whiteList);
if (info.getProfileName() == null) {
return null;
}
return info;
}
private static Set<String> initializePackages(String fileName) {
Set<String> knownPackages = null;
try {
Properties allProperties = new Properties();
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));
allProperties.load(inputStream);
inputStream.close();
String property = allProperties.getProperty(OTHER_PACKAGES);
if (property != null && property.length() != 0) {
String[] packages = property.split(","); //$NON-NLS-1$
for (int i = 0, max = packages.length; i < max; i++) {
if (knownPackages == null) {
knownPackages = new HashSet<String>();
}
knownPackages.add(packages[i]);
}
} else {
knownPackages = Collections.emptySet();
}
} catch(IOException e) {
// ignore
}
return knownPackages;
}
private static Set<String> initializeWhiteList(String fileName) {
if (fileName == null) {
return Collections.emptySet();
}
Set<String> values = new HashSet<String>();
LineNumberReader reader = null;
try {
reader = new LineNumberReader(new BufferedReader(new FileReader(fileName)));
String line;
while ((line = reader.readLine()) != null) {
String trimmedLine = line.trim();
if (!trimmedLine.isEmpty()) {
// only non-empty lines are added trimmed on both ends
values.add(trimmedLine);
}
}
} catch(IOException e) {
// ignore
} finally {
if (reader != null) {
try {
reader.close();
} catch(IOException e) {
// ignore
}
}
}
return values;
}
File[] allFiles;
// set of type names that don't have javadoc
Set<String> blackList;
String cacheLocation;
Map<String, Package> data;
String docRoot;
long generatedSize;
String JREdoc;
String JRElib;
String JREURL;
String OSGiProfile;
String profileName;
long totalSize;
Set<String> whiteList;
private ProfileInfo(
String profilename,
String jreLib,
String osgiProfile,
String jreDoc,
String jreURL,
String docroot,
String cacheloc,
String whitelist) {
this.JREdoc = jreDoc;
this.JREURL = jreURL;
this.JRElib = jreLib;
this.OSGiProfile = osgiProfile;
this.docRoot = docroot;
this.profileName = profilename;
this.cacheLocation = cacheloc;
this.whiteList = initializeWhiteList(whitelist);
}
private void addToBlackList(String typeName) {
if (this.blackList != null) {
this.blackList.add(typeName);
return;
}
this.blackList = new TreeSet<String>();
this.blackList.add(typeName);
}
public void dumpToCache(String typeName, char[] contents) {
if (!CACHE_ENABLED || this.cacheLocation == null) {
return;
}
File cacheDir = new File(this.cacheLocation);
if (!cacheDir.exists()) {
if (!cacheDir.mkdirs()) {
System.err.println("Cache creation failed for " + typeName + " for profile " + this.getProfileName()); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
}
File profileCache = new File(cacheDir, getProfileFileName());
if (!profileCache.exists()) {
if (!profileCache.mkdirs()) {
System.err.println("Cache creation failed for " + typeName + " for profile " + this.getProfileName()); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
}
File docType = new File(profileCache, typeName);
if (docType.exists()) {
// already in the cache
return;
} else {
File parentFile = docType.getParentFile();
if (!parentFile.exists()) {
if (!parentFile.mkdirs()) {
System.err.println("Cache creation failed for " + typeName + " for profile " + this.getProfileName()); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
}
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(docType));
writer.write(contents);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch(IOException e) {
// ignore
}
}
}
}
}
void generateEEDescription(String outputDir) {
if (this.data == null) {
System.err.println("No data to persist for " + this.getProfileName()); //$NON-NLS-1$
return;
}
String subDir = getSubDir();
persistData(outputDir, subDir);
persistDataAsClassFilesInZipFormat(outputDir, subDir);
}
public Map<String,Type> getAllTypes() {
Map<String,Type> result = new HashMap<String,Type>();
Set<String> keySet = this.data.keySet();
String[] sortedKeys = new String[keySet.size()];
keySet.toArray(sortedKeys);
Arrays.sort(sortedKeys);
for (int i = 0, max = sortedKeys.length; i < max; i++) {
String key = sortedKeys[i];
Package package1 = this.data.get(key);
if (package1 != null) {
package1.collectTypes(result);
} else {
System.err.println("Missing package for profile info XML serialization: " + key); //$NON-NLS-1$
}
}
return result;
}
private File getCacheRoot() {
File cacheDir = new File(this.cacheLocation);
if (cacheDir.exists() || cacheDir.mkdirs()) {
File profileCache = new File(cacheDir, getProfileFileName());
if (profileCache.exists() || profileCache.mkdirs()) {
return profileCache;
}
}
return null;
}
public byte[] getClassFileBytes(Type type) {
if (this.allFiles == null) {
throw new IllegalStateException("No jar files to open"); //$NON-NLS-1$
}
String typeName = new String(type.name);
byte[] classFileBytes = null;
try {
String zipFileEntryName = typeName.replace('.', '/') + ".class"; //$NON-NLS-1$
loop: for (int i = 0, max = this.allFiles.length; i < max; i++) {
ZipFile zipFile = new ZipFile(allFiles[i]);
try {
ZipEntry zipEntry = zipFile.getEntry(zipFileEntryName);
if (zipEntry == null) continue loop;
InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry));
try {
classFileBytes = Util.getInputStreamAsByteArray(inputStream, -1);
break loop;
} finally {
inputStream.close();
}
} finally {
zipFile.close();
}
}
} catch (ZipException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (classFileBytes == null) {
throw new IllegalStateException("Could not retrieve byte[] for " + typeName); //$NON-NLS-1$
}
ClassReader classReader = new ClassReader(classFileBytes);
StubClassAdapter visitor = new StubClassAdapter(type);
classReader.accept(visitor, ClassReader.SKIP_DEBUG);
if (visitor.shouldIgnore()) {
return null;
}
return visitor.getStub().getBytes();
}
public char[] getFromCache(String typeName) {
if (!CACHE_ENABLED || this.cacheLocation == null) {
return null;
}
File profileCache = getCacheRoot();
if (profileCache != null) {
File docType = new File(profileCache, typeName);
if (docType.exists()) {
BufferedInputStream stream = null;
try {
stream = new BufferedInputStream(new FileInputStream(docType));
return Util.getInputStreamAsCharArray(stream, -1, null);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (stream != null) {
try {
stream.close();
} catch(IOException e) {
// ignore
}
}
}
}
}
return null;
}
char[] getOnlineDocContents(String docURL, String typeName) {
// check the doc online
String typeDocURL = docURL + typeName;
char[] contents = this.getFromCache(typeName);
if (contents == null && !ONLY_USE_CACHE) {
// check the black list
if (this.isOnBlackList(typeName)) {
return null;
}
contents = Util.getURLContents(typeDocURL);
if (contents == null) {
this.addToBlackList(typeName);
return null;
} else {
this.dumpToCache(typeName, contents);
}
}
return contents;
}
public String getProfileFileName() {
return Util.getProfileFileName(getProfileName());
}
public String getProfileName() {
return this.profileName;
}
private String getSubDir() {
if (Util.getProfileFileName(ProfileModifiers.CDC_1_0_FOUNDATION_1_0_NAME).equals(this.profileName)
|| Util.getProfileFileName(ProfileModifiers.CDC_1_1_FOUNDATION_1_1_NAME).equals(this.profileName)) {
return CDC_SUBDIR;
} else if (Util.getProfileFileName(ProfileModifiers.OSGI_MINIMUM_1_0_NAME).equals(this.profileName)
|| Util.getProfileFileName(ProfileModifiers.OSGI_MINIMUM_1_1_NAME).equals(this.profileName)
|| Util.getProfileFileName(ProfileModifiers.OSGI_MINIMUM_1_2_NAME).equals(this.profileName)) {
return OSGI_SUBDIR;
} else {
return JRE_SUBDIR;
}
}
private Set<String> initializeBlackList() {
File cacheRoot = getCacheRoot();
File blackListFile = new File(cacheRoot, BLACK_LIST_NAME);
Set<String> values = new TreeSet<String>();
if (blackListFile.exists()) {
LineNumberReader reader = null;
try {
reader = new LineNumberReader(new BufferedReader(new FileReader(blackListFile)));
String line;
while ((line = reader.readLine()) != null) {
String trimmedLine = line.trim();
if (!trimmedLine.isEmpty()) {
// only non-empty lines are added trimmed on both ends
values.add(trimmedLine);
}
}
} catch(IOException e) {
// ignore
} finally {
if (reader != null) {
try {
reader.close();
} catch(IOException e) {
// ignore
}
}
}
}
return values;
}
public void initializeData() throws IOException {
String pname = this.getProfileName();
if (pname == null) {
// invalid profile info
System.err.println("Info are invalid"); //$NON-NLS-1$
return;
}
if (DEBUG) {
System.out.println("Profile : " + pname); //$NON-NLS-1$
}
long time = System.currentTimeMillis();
this.allFiles = Util.getAllFiles(new File(this.JRElib), new FileFilter() {
public boolean accept(File pathname) {
return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".jar"); //$NON-NLS-1$
}
});
if (allFiles == null) {
System.err.println("No jar files to proceed"); //$NON-NLS-1$
return;
}
// initialize known packages
String osgiProfileName = this.OSGiProfile;
Set<String> knownPackages = initializePackages(osgiProfileName);
// known packages should be part of the white list by default
if (this.whiteList != null && !this.whiteList.isEmpty()) {
this.whiteList.addAll(knownPackages);
} else {
this.whiteList = Collections.unmodifiableSet(knownPackages);
}
Map<String, Type> allVisibleTypes = new HashMap<String, Type>();
Map<String, Type> allTypes = new HashMap<String, Type>();
this.totalSize = 0;
for (int i = 0, max = allFiles.length; i < max; i++) {
File currentFile = allFiles[i];
this.totalSize += currentFile.length();
ZipFile zipFile = new ZipFile(currentFile);
try {
for (Enumeration<? extends ZipEntry> enumeration = zipFile.entries(); enumeration.hasMoreElements(); ) {
ZipEntry zipEntry = enumeration.nextElement();
if (!zipEntry.getName().endsWith(".class")) continue; //$NON-NLS-1$
InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry));
IClassFileReader classFileReader = null;
try {
classFileReader = ToolFactory.createDefaultClassFileReader(inputStream, IClassFileReader.ALL_BUT_METHOD_BODIES);
} finally {
inputStream.close();
}
if (classFileReader == null) continue;
char[] className = classFileReader.getClassName();
char[] packageName = null;
int lastIndexOf = CharOperation.lastIndexOf('/', className);
if (lastIndexOf == -1) {
packageName = new char[0];
} else {
packageName = CharOperation.subarray(className, 0, lastIndexOf);
}
if (this.isMatching(knownPackages, packageName)) {
Type type = Type.newType(this, classFileReader, this.JREdoc, this.JREURL, this.docRoot);
if (type != null) {
if (type.isProtected() || type.isPublic()) {
allVisibleTypes.put(type.getFullQualifiedName(), type);
}
allTypes.put(type.getFullQualifiedName(), type);
}
}
}
} finally {
zipFile.close();
}
}
// list all results
List<Type> visibleTypes = new ArrayList<Type>();
visibleTypes.addAll(allVisibleTypes.values());
// check transitive closure to make sure all methods/fields from superclass are visible when resolving fields/methods
while (!visibleTypes.isEmpty()) {
Type type = visibleTypes.remove(0);
String superclassName = type.getSuperclassName();
while (superclassName != null) {
if (allVisibleTypes.get(superclassName) == null) {
// look for required type
Type currentSuperclass = allTypes.get(superclassName);
if (currentSuperclass == null) {
if (DEBUG) {
System.out.println("Missing type: " + superclassName); //$NON-NLS-1$
}
break;
} else {
allVisibleTypes.put(currentSuperclass.getFullQualifiedName(), currentSuperclass);
visibleTypes.add(currentSuperclass);
if (DEBUG) {
System.out.println("Reinject type: " + currentSuperclass.getFullQualifiedName()); //$NON-NLS-1$
}
}
}
Type superclass = allVisibleTypes.get(superclassName);
superclassName = superclass.getSuperclassName();
}
}
List<Type> isInDoc = new ArrayList<Type>();
ZipFile docZip = null;
if (this.JREdoc != null) {
try {
docZip = new ZipFile(this.JREdoc);
} catch(FileNotFoundException e) {
// no zip file
}
}
try {
for (Type type : allVisibleTypes.values()) {
if (checkDocStatus(this, type, docZip, this.JREURL, this.docRoot)) {
isInDoc.add(type);
}
}
} finally {
if (docZip != null) {
docZip.close();
}
}
HashMap<String, Package> typesPerPackage = new HashMap<String, Package>();
for (Iterator<Type> iterator = isInDoc.iterator(); iterator.hasNext();) {
Type type = iterator.next();
String packageName = type.getPackage();
Package package1 = typesPerPackage.get(packageName);
if (package1 == null) {
package1 = new Package(packageName);
typesPerPackage.put(packageName, package1);
}
package1.addType(type);
}
this.data = typesPerPackage;
if (DEBUG) {
System.out.println("Time spent for gathering datas for " + pname + " : " + (System.currentTimeMillis() - time) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
if (this.blackList != null) {
// persist black list
this.persistBlackList();
}
}
private boolean isMatching(Set<String> knownPackages, char[] packageName) {
if (CharOperation.indexOf("java/".toCharArray(), packageName, true) == 0) { //$NON-NLS-1$
return true;
}
if (knownPackages.isEmpty()) {
return false;
}
String currentPackage = new String(CharOperation.replaceOnCopy(packageName, '/', '.'));
return knownPackages.contains(currentPackage) || this.isOnWhiteList(currentPackage);
}
private boolean isOnBlackList(String typeName) {
if (this.blackList != null) {
return this.blackList.contains(typeName);
}
// retrieve black list if it exists
this.blackList = initializeBlackList();
return this.blackList.contains(typeName);
}
private boolean isOnWhiteList(String packageName) {
return packageName.startsWith("java.") || packageName.startsWith("javax.") || this.whiteList.contains(packageName); //$NON-NLS-1$ //$NON-NLS-2$
}
private boolean isOnWhiteList(Type type) {
return isOnWhiteList(type.getPackage());
}
private void persistBlackList() {
if (this.blackList == null || this.blackList.isEmpty()) {
return;
}
File cacheRoot = getCacheRoot();
File blackListFile = new File(cacheRoot, BLACK_LIST_NAME);
if (!blackListFile.exists()) {
PrintWriter writer = null;
try {
writer = new PrintWriter(new BufferedWriter(new FileWriter(blackListFile)));
for (Iterator<String> iterator = this.blackList.iterator(); iterator.hasNext();) {
writer.println(iterator.next());
}
writer.flush();
} catch(IOException e) {
// ignore
} finally {
if (writer != null) {
writer.close();
}
}
}
}
private void persistChildren(
Document document,
Element xmlElement,
Map<String, Package> packageMap,
String OSGiProfileName) {
Set<String> keySet = packageMap.keySet();
String[] sortedKeys = new String[keySet.size()];
keySet.toArray(sortedKeys);
Arrays.sort(sortedKeys);
for (int i = 0, max = sortedKeys.length; i < max; i++) {
String key = sortedKeys[i];
Package package1 = packageMap.get(key);
if (package1 != null) {
package1.persistXML(document, xmlElement, OSGiProfileName);
} else {
System.err.println("Missing package for profile info XML serialization: " + key); //$NON-NLS-1$
}
}
}
private void persistChildrenAsClassFile(
ZipOutputStream zipOutputStream,
Map<String, Package> packageMap,
ProfileInfo info) throws IOException {
Set<String> keySet = packageMap.keySet();
String[] sortedKeys = new String[keySet.size()];
keySet.toArray(sortedKeys);
Arrays.sort(sortedKeys);
for (int i = 0, max = sortedKeys.length; i < max; i++) {
String key = sortedKeys[i];
Package package1 = packageMap.get(key);
if (package1 != null) {
package1.persistAsClassStubsForZip(zipOutputStream, info);
} else {
System.err.println("Missing package for profile info zip serialization: " + key); //$NON-NLS-1$
}
}
}
private void persistData(String rootName, String subDirName) {
try {
Document document = org.eclipse.pde.api.tools.internal.util.Util.newDocument();
Element component = document.createElement(IApiXmlConstants.ELEMENT_COMPONENT);
String profileName2 = this.getProfileName();
component.setAttribute(IApiXmlConstants.ATTR_ID, profileName2);
component.setAttribute(IApiXmlConstants.ATTR_VERSION, IApiXmlConstants.API_DESCRIPTION_CURRENT_VERSION);
document.appendChild(component);
persistChildren(document, component, this.data, profileName2);
String contents = org.eclipse.pde.api.tools.internal.util.Util.serializeDocument(document);
String fileName = profileName2 + ".xml"; //$NON-NLS-1$
Util.write(rootName, subDirName, fileName, contents);
} catch (DOMException e) {
e.printStackTrace();
} catch (CoreException e) {
e.printStackTrace();
}
}
private void persistDataAsClassFilesInZipFormat(
String rootName,
String subDirName) {
ZipOutputStream zipOutputStream = null;
String profileName2 = this.getProfileFileName();
File root = new File(rootName);
if (!root.exists()) {
root.mkdirs();
}
File subDir = new File(root, subDirName);
if (!subDir.exists()) {
subDir.mkdir();
}
if (profileName2.indexOf('/') != 0) {
profileName2 = profileName2.replace('/', '_');
}
File file = new File(subDir, profileName2 + ".zip"); //$NON-NLS-1$
try {
zipOutputStream = Util.getOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if (zipOutputStream == null) {
System.err.println("Could not create the output file : " + file.getAbsolutePath()); //$NON-NLS-1$
return;
}
try {
persistChildrenAsClassFile(zipOutputStream, this.data, this);
zipOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
zipOutputStream.close();
} catch (IOException e) {
// ignore
}
}
this.generatedSize = file.length();
System.out.println("The stub for the profile " + this.profileName + " was generated from " + this.totalSize + " bytes."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
System.out.println("Its generated size is " + this.generatedSize + " bytes."); //$NON-NLS-1$ //$NON-NLS-2$
System.out.println("Ratio : " + (((double) this.generatedSize / (double) this.totalSize) * 100.0) + "%"); //$NON-NLS-1$ //$NON-NLS-2$
}
public String toString() {
return this.getProfileName();
}
}
static class StubClass {
private static final int CURRENT_VERSION = 3;
int access;
int classNameIndex;
List<StubField> fields;
int[] interfacesIndexes;
List<StubMethod> methods;
Map<String, Integer> pool;
int poolIndex;
int superNameIndex;
public StubClass(int acc,
String className2,
String superName2,
String[] interfaces2) {
this.access = acc;
this.pool = new HashMap<String, Integer>();
this.classNameIndex = getIndex(className2);
this.superNameIndex = superName2 != null ? getIndex(superName2) : -1;
if (interfaces2 != null) {
this.interfacesIndexes = new int[interfaces2.length];
for (int i = 0; i < interfaces2.length; i++) {
this.interfacesIndexes[i] = getIndex(interfaces2[i]);
}
}
}
public void addField(String fieldName) {
if (this.fields == null) {
this.fields = new ArrayList<StubField>();
}
this.fields.add(new StubField(getIndex(fieldName)));
}
public StubMethod addMethod(String methodName, String desc) {
if (this.methods == null) {
this.methods = new ArrayList<StubMethod>();
}
StubMethod method = new StubMethod(getIndex(methodName), getIndex(desc));
this.methods.add(method);
return method;
}
public byte[] getBytes() {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutputStream outputStream = new DataOutputStream(byteArrayOutputStream);
try {
outputStream.writeShort(CURRENT_VERSION);
outputStream.writeShort(this.poolIndex);
for (Iterator<Map.Entry<String, Integer>> iterator = this.pool.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<String, Integer> next = iterator.next();
outputStream.writeUTF(next.getKey());
outputStream.writeShort(next.getValue().intValue());
}
outputStream.writeChar(this.access);
outputStream.writeShort(this.classNameIndex);
outputStream.writeShort(this.superNameIndex);
int length = this.interfacesIndexes != null ? this.interfacesIndexes.length : 0;
outputStream.writeShort(length);
for (int i = 0; i < length; i++) {
outputStream.writeShort(this.interfacesIndexes[i]);
}
int fieldsLength = this.fields == null ? 0 : this.fields.size();
outputStream.writeShort(fieldsLength);
for (int i = 0; i < fieldsLength; i++) {
outputStream.writeShort(this.fields.get(i).nameIndex);
}
int methodsLength = this.methods == null ? 0 : this.methods.size();
outputStream.writeShort(methodsLength);
for (int i = 0; i < methodsLength; i++) {
StubMethod stubMethod = this.methods.get(i);
outputStream.writeShort(stubMethod.selectorIndex);
outputStream.writeShort(stubMethod.signatureIndex);
outputStream.writeByte(stubMethod.isPolymorphic ? 1 : 0);
}
outputStream.flush();
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
outputStream.close();
} catch(IOException e) {
// ignore
}
}
this.pool = null;
this.fields = null;
this.methods = null;
return byteArrayOutputStream.toByteArray();
}
private int getIndex(String name) {
Integer integer = this.pool.get(name);
if (integer != null) {
return integer.intValue();
}
int value = this.poolIndex++;
this.pool.put(name, new Integer(value));
return value;
}
}
/**
* Class adapter
*/
static class StubClassAdapter extends ClassAdapter {
static final int IGNORE_CLASS_FILE = 0x100;
int flags;
String name;
StubClass stub;
Type type;
/**
* Constructor
* @param stubtype
*/
public StubClassAdapter(Type stubtype) {
super(new ClassWriter(0));
this.type = stubtype;
}
public StubClass getStub() {
return this.stub;
}
/**
* @return if this class file should be ignored or not
*/
public boolean shouldIgnore() {
return (this.flags & IGNORE_CLASS_FILE) != 0;
}
/* (non-Javadoc)
* @see org.objectweb.asm.ClassAdapter#visit(int, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
*/
public void visit(int version,
int access,
String className,
String signature,
String superName,
String[] interfaces) {
this.name = className;
this.stub = new StubClass(access, className, superName, interfaces);
}
@Override
public AnnotationVisitor visitAnnotation(String arg0, boolean arg1) {
return null;
}
/* (non-Javadoc)
* @see org.objectweb.asm.ClassAdapter#visitAttribute(org.objectweb.asm.Attribute)
*/
public void visitAttribute(Attribute attr) {
if ("Synthetic".equals(attr.type)) { //$NON-NLS-1$
this.flags |= IGNORE_CLASS_FILE;
}
}
@Override
public void visitEnd() {
}
/* (non-Javadoc)
* @see org.objectweb.asm.ClassAdapter#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
*/
public FieldVisitor visitField(int access, String fieldName, String desc, String signature, Object value) {
if (type.getField(fieldName) == null) {
return null;
}
this.stub.addField(fieldName);
return null;
}
/* (non-Javadoc)
* @see org.objectweb.asm.ClassAdapter#visitInnerClass(java.lang.String, java.lang.String, java.lang.String, int)
*/
public void visitInnerClass(String innerClassName, String outerName, String innerName, int access) {
if (this.name.equals(innerClassName) && (outerName == null)) {
// local class
this.flags |= IGNORE_CLASS_FILE;
}
}
/* (non-Javadoc)
* @see org.objectweb.asm.ClassAdapter#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
*/
public MethodVisitor visitMethod(int access,
String methodName,
String desc,
String signature,
String[] exceptions) {
if ("<clinit>".equals(methodName)) { //$NON-NLS-1$
return null;
}
if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
return null;
}
if (((access & Opcodes.ACC_BRIDGE) != 0)
|| ((access & Opcodes.ACC_SYNTHETIC) != 0)) {
return null;
}
if (this.type.getMethod(methodName, desc) == null) {
return null;
}
final StubMethod method = this.stub.addMethod(methodName, desc);
return new MethodAdapter(super.visitMethod(access, methodName, desc, signature, exceptions)) {
@Override
public AnnotationVisitor visitAnnotation(String sig, boolean visible) {
if (visible && "Ljava/lang/invoke/MethodHandle$PolymorphicSignature;".equals(sig)) { //$NON-NLS-1$
method.isPolymorphic();
}
return super.visitAnnotation(sig, visible);
}
};
}
@Override
public void visitOuterClass(String arg0, String arg1, String arg2) {
// ignore
}
@Override
public void visitSource(String arg0, String arg1) {
// ignore
}
}
static class StubField {
int nameIndex;
public StubField(int index) {
this.nameIndex = index;
}
}
static class StubMethod {
boolean isPolymorphic;
int selectorIndex;
int signatureIndex;
public StubMethod(int selectorindex, int sigindex) {
this.selectorIndex = selectorindex;
this.signatureIndex = sigindex;
}
public void isPolymorphic() {
this.isPolymorphic = true;
}
}
static class Type extends AbstractNode implements Comparable<Type> {
static boolean isFinal(int accessFlags) {
return (accessFlags & Flags.AccFinal) != 0;
}
static boolean isPrivate(int accessFlags) {
return (accessFlags & Flags.AccPrivate) != 0;
}
static boolean isProtected(int accessFlags) {
return (accessFlags & Flags.AccProtected) != 0;
}
static boolean isPublic(int accessFlags) {
return (accessFlags & Flags.AccPublic) != 0;
}
static boolean isStatic(int accessFlags) {
return (accessFlags & Flags.AccStatic) != 0;
}
private static boolean isVisibleField(int typeAccessFlags, int fieldAccessFlags) {
if (isPublic(fieldAccessFlags)) {
return true;
}
if (isProtected(fieldAccessFlags)) {
return !isFinal(typeAccessFlags);
}
return false;
}
private static boolean isVisibleMethod(int typeAccessFlags, int methodAccessFlags) {
if (isPublic(methodAccessFlags)) {
return true;
}
if (isProtected(methodAccessFlags)) {
return !isFinal(typeAccessFlags);
}
return false;
}
public static Type newType(
ProfileInfo info,
IClassFileReader reader,
String docZipFileName,
String docURL,
String docRoot) {
int startingIndex = 0;
IInnerClassesAttribute innerClassesAttribute = reader.getInnerClassesAttribute();
if (innerClassesAttribute != null) {
// search the right entry
IInnerClassesAttributeEntry[] entries = innerClassesAttribute.getInnerClassAttributesEntries();
for (int i = 0, max = entries.length; i < max ; i++) {
IInnerClassesAttributeEntry entry = entries[i];
char[] innerClassName = entry.getInnerClassName();
if (innerClassName != null) {
if (CharOperation.equals(reader.getClassName(), innerClassName)) {
int accessFlags2 = entry.getAccessFlags();
if (entry.getOuterClassName() != null) {
if (!isStatic(accessFlags2)) {
startingIndex = 1;
}
}
if (isPrivate(accessFlags2)) {
return null;
}
}
}
}
}
return new Type(info, startingIndex, reader, docZipFileName, docURL, docRoot);
}
Set<Field> fields;
Set<Method> methods;
int modifiers;
char[] name;
char[] superclassName;
char[][] superinterfacesNames;
private Type(
ProfileInfo info,
int startingIndex,
IClassFileReader reader,
String docZipFileName,
String docURL,
String docRoot) {
ZipFile docZip = null;
try {
if (docZipFileName != null) {
try {
docZip = new ZipFile(docZipFileName);
} catch(FileNotFoundException e) {
// no zip file
}
}
char[] className = reader.getClassName();
className = CharOperation.replaceOnCopy(className, '/', '.');
this.name = className;
if (DEBUG) {
System.out.println("Adding type: " + String.valueOf(className)); //$NON-NLS-1$
}
char[] scname = reader.getSuperclassName();
if (scname != null) {
scname = CharOperation.replaceOnCopy(scname, '/', '.');
this.superclassName = scname;
}
char[][] interfaceNames = CharOperation.deepCopy(reader.getInterfaceNames());
for (int i = 0, max = interfaceNames.length; i < max; i++) {
CharOperation.replace(interfaceNames[i], '/', '.');
}
this.superinterfacesNames = interfaceNames;
this.modifiers = reader.getAccessFlags();
IFieldInfo[] fieldInfos = reader.getFieldInfos();
int length = fieldInfos.length;
for (int i = 0; i < length; i++) {
IFieldInfo fieldInfo = fieldInfos[i];
if (isVisibleField(this.modifiers, fieldInfo.getAccessFlags())) {
if (fields == null) {
this.fields = new HashSet<Field>();
}
Field field = new Field(fieldInfo.getName(), fieldInfo.getDescriptor());
fields.add(field);
if (DEBUG) {
System.out.println("Adding field: " + field); //$NON-NLS-1$
}
}
}
IMethodInfo[] methodInfos = reader.getMethodInfos();
length = methodInfos.length;
for (int i = 0, max = methodInfos.length; i < max; i++) {
IMethodInfo methodInfo = methodInfos[i];
IClassFileAttribute[] attributes = methodInfo.getAttributes();
ISignatureAttribute signatureAttribute = null;
for (int j = 0, max2 = attributes.length; j < max2; j++) {
IClassFileAttribute currentAttribute = attributes[j];
if (CharOperation.equals(currentAttribute.getAttributeName(), IAttributeNamesConstants.SIGNATURE)) {
signatureAttribute = (ISignatureAttribute) currentAttribute;
break;
}
}
char[] signature = null;
if (signatureAttribute != null) {
signature = signatureAttribute.getSignature();
} else {
signature = methodInfo.getDescriptor();
}
int accessFlags = methodInfo.getAccessFlags();
if (isVisibleMethod(this.modifiers, accessFlags)) {
if (methods == null) {
this.methods = new HashSet<Method>();
}
Method method = new Method(
accessFlags,
methodInfo.getName(),
methodInfo.getDescriptor(),
signatureAttribute == null ? null : signature);
methods.add(method);
if (DEBUG) {
System.out.println("Adding method: " + method); //$NON-NLS-1$
}
}
}
} catch(IOException e) {
// no zip file
System.err.println("Missing doc zip at " + docZipFileName); //$NON-NLS-1$
} finally {
if (docZip != null) {
try {
docZip.close();
} catch (IOException e) {
// ignore
}
}
}
}
public void addField(Field f) {
if (this.fields == null) {
this.fields = new HashSet<Field>();
}
this.fields.add(f);
}
public void addMethod(Method m) {
if (this.methods == null) {
this.methods = new HashSet<Method>();
}
this.methods.add(m);
}
public int compareTo(Type type) {
return this.getSimpleName().compareTo(type.getSimpleName());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Type)) {
return false;
}
Type other = (Type) obj;
return Arrays.equals(name, other.name);
}
public Field getField(String fname) {
if (this.fields == null) return null;
Field fieldToFind = new Field(fname.toCharArray(), null);
for (Iterator<Field> iterator = this.fields.iterator(); iterator.hasNext(); ) {
Field currentField = iterator.next();
if (fieldToFind.equals(currentField)) {
return currentField;
}
}
return null;
}
public String getFullQualifiedName() {
return String.valueOf(this.name);
}
public String getSuperclassName() {
if (this.superclassName == null) return null;
return String.valueOf(this.superclassName);
}
public Method getMethod(String selector, String signature) {
if (this.methods == null) return null;
Method methodToFind = new Method(0, selector.toCharArray(), signature.toCharArray(), null);
for (Iterator<Method> iterator = this.methods.iterator(); iterator.hasNext(); ) {
Method currentMethod = iterator.next();
if (methodToFind.equals(currentMethod)) {
return currentMethod;
}
}
return null;
}
public String getPackage() {
int index = CharOperation.lastIndexOf('.', this.name);
return new String(CharOperation.subarray(this.name, 0, index));
}
public String getSimpleName() {
return Util.getSimpleName(this.name);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(name);
return result;
}
public boolean isProtected() {
return isProtected(this.modifiers);
}
public boolean isPublic() {
return isPublic(this.modifiers);
}
public void persistXML(Document document, Element parent, String OSGiProfileName) {
Element type = document.createElement(IApiXmlConstants.ELEMENT_TYPE);
parent.appendChild(type);
type.setAttribute(IApiXmlConstants.ATTR_NAME, getSimpleName());
if (this.superclassName != null) {
type.setAttribute(IApiXmlConstants.ATTR_SUPER_CLASS, new String(this.superclassName));
}
if (this.superinterfacesNames != null && this.superinterfacesNames.length != 0) {
type.setAttribute(IApiXmlConstants.ATTR_SUPER_INTERFACES, Util.getInterfaces(this.superinterfacesNames));
}
type.setAttribute(IApiXmlConstants.ATTR_INTERFACE, Boolean.toString((this.modifiers & Flags.AccInterface) != 0));
persistAnnotations(type, OSGiProfileName);
if (this.fields != null) {
Field[] allFields = new Field[this.fields.size()];
this.fields.toArray(allFields);
Arrays.sort(allFields);
for (int i = 0, max = allFields.length; i < max; i++) {
allFields[i].persistXML(document, type, OSGiProfileName);
}
}
if (this.methods != null) {
Method[] allMethods = new Method[this.methods.size()];
this.methods.toArray(allMethods);
Arrays.sort(allMethods);
for (int i = 0, max = allMethods.length; i < max; i++) {
allMethods[i].persistXML(document, type, OSGiProfileName);
}
}
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(this.getPackage()+ "." + this.getSimpleName()).append(Util.LINE_SEPARATOR); //$NON-NLS-1$
// list all fields
if (this.fields != null) {
Field[] allFields = new Field[this.fields.size()];
this.fields.toArray(allFields);
Arrays.sort(allFields);
for (int i = 0, max = allFields.length; i < max; i++) {
Field field = allFields[i];
buffer
.append("\t") //$NON-NLS-1$
.append(field)
.append(Util.LINE_SEPARATOR);
}
}
if (this.methods != null) {
Method[] allMethods = new Method[this.methods.size()];
this.methods.toArray(allMethods);
Arrays.sort(allMethods);
for (int i = 0, max = allMethods.length; i < max; i++) {
Method method = allMethods[i];
buffer.append("\t").append(method).append(Util.LINE_SEPARATOR); //$NON-NLS-1$
}
}
return String.valueOf(buffer);
}
}
static SortedSet<String> ACCEPTED_EEs;
static boolean CACHE_ENABLED = true;
static boolean DEBUG = false;
static boolean ONLY_USE_CACHE = false;
static final String PROPERTY_CACHE_LOCATION = ".cacheLocation"; //$NON-NLS-1$
static final String PROPERTY_DOC_ROOT = ".docRoot"; //$NON-NLS-1$
static final String PROPERTY_JRE_DOC = ".jreDoc"; //$NON-NLS-1$
static final String PROPERTY_JRE_LIB = ".jreLib"; //$NON-NLS-1$
static final String PROPERTY_JRE_URL = ".jreURL"; //$NON-NLS-1$
static final String PROPERTY_OSGI_PROFILE = ".osgiProfile"; //$NON-NLS-1$
static final String PROPERTY_WHITE_LIST = ".whiteList"; //$NON-NLS-1$
static {
String[] ees = new String[] {
"JRE-1.1", //$NON-NLS-1$
"J2SE-1.2", //$NON-NLS-1$
"J2SE-1.3", //$NON-NLS-1$
"J2SE-1.4", //$NON-NLS-1$
"J2SE-1.5", //$NON-NLS-1$
"JavaSE-1.6", //$NON-NLS-1$
"JavaSE-1.7", //$NON-NLS-1$
"JavaSE-1.8", //$NON-NLS-1$
"CDC-1.0_Foundation-1.0", //$NON-NLS-1$
"CDC-1.1_Foundation-1.1", //$NON-NLS-1$
"OSGi_Minimum-1.0", //$NON-NLS-1$
"OSGi_Minimum-1.1", //$NON-NLS-1$
"OSGi_Minimum-1.2" //$NON-NLS-1$
};
ACCEPTED_EEs = new TreeSet<String>();
for (String ee : ees) {
ACCEPTED_EEs.add(ee);
}
}
private static String getAllEEValues() {
StringBuffer buffer = new StringBuffer();
for (String ee : ACCEPTED_EEs) {
if (buffer.length() != 0) {
buffer.append(", "); //$NON-NLS-1$
}
buffer.append(ee);
}
return String.valueOf(buffer);
}
public static void main(String[] args) throws IOException {
EEGenerator generator = new EEGenerator();
generator.configure(args);
if (!generator.isInitialized()) {
System.err.println("Usage: -output <path to root to output files> -config <path to configuration file> -EEs <list of EE to generate separated with commas>"); //$NON-NLS-1$
return;
}
String property = System.getProperty("DEBUG"); //$NON-NLS-1$
DEBUG = (property != null) && "true".equalsIgnoreCase(property); //$NON-NLS-1$
generator.run();
}
private ProfileInfo[] allProfiles;
String configurationFile;
String[] EEToGenerate;
String output;
private boolean checkFileProperty(String property) {
if (property == null) {
return false;
}
File jreDocFile = new File(property);
return jreDocFile.exists() && jreDocFile.isFile();
}
private boolean checkJREProperty(String property) {
if (property == null) {
return false;
}
File jreLibFolder = new File(property);
return jreLibFolder.exists() && jreLibFolder.isDirectory();
}
private void configure(String[] args) {
String currentArg = null;
int argCount = args.length;
int index = -1;
final int DEFAULT = 0;
final int OUTPUT = 1;
final int CONFIG = 2;
final int EEs = 3;
int mode = DEFAULT;
while (++index < argCount) {
currentArg = args[index];
switch(mode) {
case DEFAULT :
if ("-output".equals(currentArg)) { //$NON-NLS-1$
if (this.output != null)
throw new IllegalArgumentException("output value is already set"); //$NON-NLS-1$
mode = OUTPUT;
continue;
}
if ("-config".equals(currentArg)) { //$NON-NLS-1$
if (this.configurationFile != null)
throw new IllegalArgumentException("configuration value is already set"); //$NON-NLS-1$
mode = CONFIG;
continue;
}
if ("-EEs".equals(currentArg)) { //$NON-NLS-1$
if (this.EEToGenerate != null)
throw new IllegalArgumentException("EEs value is already set"); //$NON-NLS-1$
mode = EEs;
continue;
}
// ignore unknown arguments - might be passed in by the Eclipse application
continue;
case OUTPUT :
this.output = currentArg;
mode = DEFAULT;
continue;
case CONFIG :
this.configurationFile = currentArg;
mode = DEFAULT;
continue;
case EEs :
String listOfEEs = currentArg;
StringTokenizer tokenizer = new StringTokenizer(listOfEEs, ","); //$NON-NLS-1$
List<String> list = new ArrayList<String>();
while (tokenizer.hasMoreTokens()) {
String currentEE = tokenizer.nextToken().trim();
if (ACCEPTED_EEs.contains(currentEE)) {
list.add(currentEE);
} else {
throw new IllegalArgumentException("Wrong EE value: " + currentEE + " accepted values are: " + getAllEEValues()); //$NON-NLS-1$ //$NON-NLS-2$
}
}
if (!list.isEmpty()) {
list.toArray(this.EEToGenerate = new String[list.size()]);
}
mode = DEFAULT;
continue;
default:
break;
}
}
if (this.output == null) {
throw new IllegalArgumentException("output value is missing"); //$NON-NLS-1$
}
// check output
File file = new File(this.output);
if (!file.exists()) {
if (!file.mkdirs()) {
this.output = null;
throw new IllegalArgumentException("Could not create the output dir"); //$NON-NLS-1$
}
}
// check configuration file
File configuration = new File(this.configurationFile);
if (!configuration.exists()) {
this.configurationFile = null;
throw new IllegalArgumentException("Configuration file doesn't exist"); //$NON-NLS-1$
}
Properties properties = new Properties();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(configuration));
properties.load(reader);
} catch(IOException e ) {
e.printStackTrace();
throw new IllegalArgumentException("Could not properly initialize the properties"); //$NON-NLS-1$
} finally {
if (reader != null) {
try {
reader.close();
} catch(IOException e) {
// ignore
}
}
}
List<ProfileInfo> infos = new ArrayList<EEGenerator.ProfileInfo>();
for (String EE : EEToGenerate) {
// Retrieve all properties for each EE
// JRELIB, OSGI_PROFILE, JRE_DOC, JRE_URL, CACHE, WHITE_LIST
String key = EE + PROPERTY_JRE_LIB;
String jreLibProperty = properties.getProperty(key, null);
if (!checkJREProperty(jreLibProperty)) {
throw new IllegalArgumentException("Wrong property value : " + key); //$NON-NLS-1$
}
key = EE + PROPERTY_CACHE_LOCATION;
String cacheLocationProperty = properties.getProperty(key, null);
if (cacheLocationProperty != null) {
if (cacheLocationProperty.isEmpty()) {
cacheLocationProperty = null;
}
}
key = EE + PROPERTY_DOC_ROOT;
String docRootProperty = properties.getProperty(key, ""); //$NON-NLS-1$
key = EE + PROPERTY_JRE_DOC;
String jreDocProperty = properties.getProperty(key, null);
if (jreDocProperty != null && !jreDocProperty.isEmpty()) {
if (!checkFileProperty(jreDocProperty)) {
throw new IllegalArgumentException("Wrong property value : " + key); //$NON-NLS-1$
}
} else {
jreDocProperty = null;
}
key = EE + PROPERTY_JRE_URL;
String jreUrlProperty = properties.getProperty(key, null);
if (jreUrlProperty != null && !jreUrlProperty.isEmpty()) {
if (Util.getURLContents(jreUrlProperty + docRootProperty + "java/lang/Object.html") == null) { //$NON-NLS-1$
throw new IllegalArgumentException("Wrong property value : " + key); //$NON-NLS-1$
}
} else {
jreUrlProperty = null;
}
key = EE + PROPERTY_OSGI_PROFILE;
String osgiProfileProperty = properties.getProperty(key, null);
if (osgiProfileProperty != null && !osgiProfileProperty.isEmpty()) {
if (!checkFileProperty(osgiProfileProperty)) {
throw new IllegalArgumentException("Wrong property value : " + key); //$NON-NLS-1$
}
} else {
osgiProfileProperty = null;
}
key = EE + PROPERTY_WHITE_LIST;
String whiteListProperty = properties.getProperty(key, null);
if (whiteListProperty != null && !whiteListProperty.isEmpty()) {
if (!checkFileProperty(whiteListProperty)) {
throw new IllegalArgumentException("Wrong property value : " + key); //$NON-NLS-1$
}
} else {
whiteListProperty = null;
}
infos.add(
ProfileInfo.getProfileInfo(
EE,
jreLibProperty,
osgiProfileProperty,
jreDocProperty,
jreUrlProperty,
docRootProperty,
cacheLocationProperty,
whiteListProperty));
}
if (infos.isEmpty()) {
throw new IllegalArgumentException("Profile infos cannot be empty"); //$NON-NLS-1$
}
infos.toArray(allProfiles = new ProfileInfo[infos.size()]);
}
private boolean isInitialized() {
return this.configurationFile != null && this.EEToGenerate != null && this.output != null;
}
private void run() {
if (allProfiles == null) {
System.err.println("No descriptions to generate"); //$NON-NLS-1$
return;
}
int numberOfProfiles = allProfiles.length;
for (int i = 0; i < numberOfProfiles; i++) {
ProfileInfo profileInfo = allProfiles[i];
if (profileInfo != null) {
try {
profileInfo.initializeData();
} catch (IOException e) {
e.printStackTrace();
}
// persist the EE description
profileInfo.generateEEDescription(this.output);
}
}
}
}