1 /*******************************************************************************
2  * Copyright (c) 2011, 2018 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.pde.api.tools.generator;
15 
16 import java.io.BufferedInputStream;
17 import java.io.BufferedReader;
18 import java.io.BufferedWriter;
19 import java.io.ByteArrayOutputStream;
20 import java.io.DataOutputStream;
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileNotFoundException;
24 import java.io.FileReader;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.LineNumberReader;
29 import java.io.PrintWriter;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collections;
33 import java.util.Enumeration;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.Iterator;
37 import java.util.LinkedHashMap;
38 import java.util.LinkedHashSet;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Map.Entry;
42 import java.util.Properties;
43 import java.util.Set;
44 import java.util.SortedSet;
45 import java.util.StringTokenizer;
46 import java.util.TreeSet;
47 import java.util.zip.ZipEntry;
48 import java.util.zip.ZipException;
49 import java.util.zip.ZipFile;
50 import java.util.zip.ZipOutputStream;
51 
52 import org.eclipse.core.runtime.CoreException;
53 import org.eclipse.jdt.core.Flags;
54 import org.eclipse.jdt.core.Signature;
55 import org.eclipse.jdt.core.ToolFactory;
56 import org.eclipse.jdt.core.compiler.CharOperation;
57 import org.eclipse.jdt.core.util.IAttributeNamesConstants;
58 import org.eclipse.jdt.core.util.IClassFileAttribute;
59 import org.eclipse.jdt.core.util.IClassFileReader;
60 import org.eclipse.jdt.core.util.IFieldInfo;
61 import org.eclipse.jdt.core.util.IInnerClassesAttribute;
62 import org.eclipse.jdt.core.util.IInnerClassesAttributeEntry;
63 import org.eclipse.jdt.core.util.IMethodInfo;
64 import org.eclipse.jdt.core.util.ISignatureAttribute;
65 import org.eclipse.pde.api.tools.generator.util.Util;
66 import org.eclipse.pde.api.tools.internal.IApiXmlConstants;
67 import org.eclipse.pde.api.tools.internal.provisional.ProfileModifiers;
68 import org.objectweb.asm.AnnotationVisitor;
69 import org.objectweb.asm.Attribute;
70 import org.objectweb.asm.ClassReader;
71 import org.objectweb.asm.ClassVisitor;
72 import org.objectweb.asm.ClassWriter;
73 import org.objectweb.asm.FieldVisitor;
74 import org.objectweb.asm.MethodVisitor;
75 import org.objectweb.asm.Opcodes;
76 import org.w3c.dom.DOMException;
77 import org.w3c.dom.Document;
78 import org.w3c.dom.Element;
79 
80 /**
81  * This is a java application to generate the EE descriptions for specified EE
82  * names:
83  * <p>
84  * Accepted names are:
85  * </p>
86  * <ol>
87  * <li>JRE-1.1,</li>
88  * <li>J2SE-1.2,</li>
89  * <li>J2SE-1.3,</li>
90  * <li>J2SE-1.4,</li>
91  * <li>J2SE-1.5,</li>
92  * <li>JavaSE-1.6,</li>
93  * <li>JavaSE-1.7,</li>
94  * <li>JavaSE-1.8,</li>
95  * <li>CDC-1.0_Foundation-1.0,</li>
96  * <li>CDC-1.1_Foundation-1.1,</li>
97  * <li>OSGi_Minimum-1.0</li>,
98  * <li>OSGi_Minimum-1.1,</li>
99  * <li>OSGi_Minimum-1.2.</li>
100  * </ol>
101  * This can be called using: -output c:/EE_descriptions -config
102  * C:\OSGi_profiles\configuration.properties -EEs
103  * JRE-1.1,J2SE-1.2,J2SE-1.3,J2SE-
104  * 1.4,J2SE-1.5,JavaSE-1.6,JavaSE-1.7,JavaSE-1.8,JavaSE-9,CDC-
105  * 1.0_Foundation-1.0,CDC-1.1_Foundation-1.1,OSGi_Minimum-1.0,OSGi_Minimum-1.1,OSGi_Minimum-1.
106  * 2
107  */
108 public class EEGenerator {
109 	static class AbstractNode {
110 		protected int addedProfileValue = -1;
111 		protected int removedProfileValue = -1;
112 
persistAnnotations(Element element)113 		public void persistAnnotations(Element element) {
114 			if (this.addedProfileValue != -1) {
115 				element.setAttribute(IApiXmlConstants.ATTR_ADDED_PROFILE, Integer.toString(this.addedProfileValue));
116 			}
117 			if (this.removedProfileValue != -1) {
118 				element.setAttribute(IApiXmlConstants.ATTR_REMOVED_PROFILE, Integer.toString(this.removedProfileValue));
119 			}
120 		}
121 
persistAnnotations(Element element, String OSGiProfileName)122 		public void persistAnnotations(Element element, String OSGiProfileName) {
123 			int value = ProfileModifiers.getValue(OSGiProfileName);
124 			if (value != -1) {
125 				element.setAttribute(IApiXmlConstants.ATTR_PROFILE, Integer.toString(value));
126 			}
127 		}
128 
setAddedProfileValue(int value)129 		public void setAddedProfileValue(int value) {
130 			if (this.addedProfileValue != -1) {
131 				System.err.println("Remove profile value is already set"); //$NON-NLS-1$
132 			}
133 			this.addedProfileValue = value;
134 		}
setRemovedProfileValue(int value)135 		public void setRemovedProfileValue(int value) {
136 			if (this.removedProfileValue != -1) {
137 				System.err.println("Remove profile value is already set"); //$NON-NLS-1$
138 			}
139 			this.removedProfileValue = value;
140 		}
141 	}
142 	static class Field extends AbstractNode implements Comparable<Field> {
143 		char[] name;
144 		char[] type;
145 
Field(char[] fname, char[] ftype)146 		Field(char[] fname, char[] ftype) {
147 			this.name = fname;
148 			if (ftype != null) {
149 				this.type = CharOperation.replaceOnCopy(ftype, '/', '.');
150 			}
151 		}
152 		@Override
compareTo(Field field)153 		public int compareTo(Field field) {
154 			return CharOperation.compareTo(this.name, field.name);
155 		}
156 		@Override
equals(Object obj)157 		public boolean equals(Object obj) {
158 			if (this == obj) {
159 				return true;
160 			}
161 			if (obj == null) {
162 				return false;
163 			}
164 			if (!(obj instanceof Field)) {
165 				return false;
166 			}
167 			Field other = (Field) obj;
168 			return Arrays.equals(name, other.name);
169 		}
170 
getStatus()171 		public int getStatus() {
172 			return -1;
173 		}
174 
175 		@Override
hashCode()176 		public int hashCode() {
177 			final int prime = 31;
178 			int result = 1;
179 			result = prime * result + Arrays.hashCode(name);
180 			result = prime * result + Arrays.hashCode(type);
181 			return result;
182 		}
183 
persistXML(Document document, Element parent)184 		public void persistXML(Document document, Element parent) {
185 			Element field = document.createElement(IApiXmlConstants.ELEMENT_FIELD);
186 			parent.appendChild(field);
187 			field.setAttribute(IApiXmlConstants.ATTR_NAME, new String(this.name));
188 			field.setAttribute(IApiXmlConstants.ATTR_STATUS, Integer.toString(getStatus()));
189 		}
190 
persistXML(Document document, Element parent, String OSGiProfileName)191 		public void persistXML(Document document, Element parent, String OSGiProfileName) {
192 			Element field = document.createElement(IApiXmlConstants.ELEMENT_FIELD);
193 			parent.appendChild(field);
194 			field.setAttribute(IApiXmlConstants.ATTR_NAME, new String(this.name));
195 			persistAnnotations(field, OSGiProfileName);
196 		}
197 
198 		@Override
toString()199 		public String toString() {
200 			StringBuilder builder = new StringBuilder();
201 			builder.append("Field : ") //$NON-NLS-1$
202 			.append(this.name).append(' ').append(Signature.toCharArray(this.type));
203 			return String.valueOf(builder);
204 		}
205 	}
206 	static class Method extends AbstractNode implements Comparable<Method> {
207 		public static final char[] NO_GENERIC_SIGNATURE = new char[0];
208 		char[] genericSignature;
209 		int modifiers;
210 		char[] selector;
211 		char[] signature;
212 
Method(int mods, char[] select, char[] sig, char[] genericsig)213 		Method(int mods, char[] select, char[] sig, char[] genericsig) {
214 			this.selector = select;
215 			this.signature = sig;
216 			this.modifiers = mods;
217 			if (genericsig == null) {
218 				this.genericSignature = NO_GENERIC_SIGNATURE;
219 			} else {
220 				this.genericSignature = genericsig;
221 			}
222 		}
223 		@Override
compareTo(Method method)224 		public int compareTo(Method method) {
225 			int compare = CharOperation.compareTo(this.selector, method.selector);
226 			if (compare == 0) {
227 				int compareTo = CharOperation.compareTo(this.signature, method.signature);
228 				if (compareTo == 0) {
229 					return this.getStatus() - method.getStatus();
230 				}
231 				return compareTo;
232 			}
233 			return compare;
234 		}
235 
236 		@Override
equals(Object obj)237 		public boolean equals(Object obj) {
238 			if (this == obj) {
239 				return true;
240 			}
241 			if (obj == null) {
242 				return false;
243 			}
244 			if (!(obj instanceof Method)) {
245 				return false;
246 			}
247 			Method other = (Method) obj;
248 			if (!Arrays.equals(selector, other.selector)) {
249 				return false;
250 			}
251 			return Arrays.equals(signature, other.signature);
252 		}
253 
getStatus()254 		public int getStatus() {
255 			return -1;
256 		}
257 
258 		@Override
hashCode()259 		public int hashCode() {
260 			final int prime = 31;
261 			int result = 1;
262 			result = prime * result + Arrays.hashCode(selector);
263 			result = prime * result + Arrays.hashCode(signature);
264 			return result;
265 		}
266 
persistXML(Document document, Element parent)267 		public void persistXML(Document document, Element parent) {
268 			Element method = document.createElement(IApiXmlConstants.ELEMENT_METHOD);
269 			parent.appendChild(method);
270 			method.setAttribute(IApiXmlConstants.ATTR_NAME, new String(this.selector));
271 			method.setAttribute(IApiXmlConstants.ATTR_SIGNATURE, new String(this.signature));
272 			method.setAttribute(IApiXmlConstants.ATTR_STATUS, Integer.toString(getStatus()));
273 		}
274 
persistXML(Document document, Element parent, String OSGiProfileName)275 		public void persistXML(Document document, Element parent, String OSGiProfileName) {
276 			Element method = document.createElement(IApiXmlConstants.ELEMENT_METHOD);
277 			parent.appendChild(method);
278 			method.setAttribute(IApiXmlConstants.ATTR_NAME, new String(this.selector));
279 			method.setAttribute(IApiXmlConstants.ATTR_SIGNATURE, new String(this.signature));
280 			persistAnnotations(method, OSGiProfileName);
281 		}
282 
283 		@Override
toString()284 		public String toString() {
285 			StringBuilder builder = new StringBuilder();
286 			builder.append("Method : "); //$NON-NLS-1$
287 			if (this.genericSignature != null && this.genericSignature.length != 0) {
288 				builder.append(Signature.toCharArray(this.genericSignature, this.selector, null, true, true));
289 			} else {
290 				builder.append(Signature.toCharArray(this.signature, this.selector, null, true, true));
291 			}
292 			return String.valueOf(builder);
293 		}
294 	}
295 
296 	static class Package extends AbstractNode implements Comparable<Package> {
297 		String name;
298 		List<Type> types;
299 
Package(String pname)300 		public Package(String pname) {
301 			this.name = pname;
302 		}
addType(Type type)303 		public void addType(Type type) {
304 			if (this.types == null) {
305 				this.types = new ArrayList<>();
306 			}
307 			this.types.add(type);
308 		}
collectTypes(Map<String, Type> result)309 		public void collectTypes(Map<String, Type> result) {
310 			Collections.sort(this.types);
311 			for (Type type : this.types) {
312 				String typeName = new String(type.name);
313 				result.put(typeName, type);
314 			}
315 		}
316 		@Override
compareTo(Package package1)317 		public int compareTo(Package package1) {
318 			return this.name.compareTo(package1.name);
319 		}
320 		@Override
equals(Object obj)321 		public boolean equals(Object obj) {
322 			if (this == obj) {
323 				return true;
324 			}
325 			if (obj == null) {
326 				return false;
327 			}
328 			if (!(obj instanceof Package)) {
329 				return false;
330 			}
331 			Package other = (Package) obj;
332 			if (name == null) {
333 				if (other.name != null) {
334 					return false;
335 				}
336 			} else if (!name.equals(other.name)) {
337 				return false;
338 			}
339 			return true;
340 		}
341 
getType(Type typeToFind)342 		public Type getType(Type typeToFind) {
343 			if (this.types == null) {
344 				return null;
345 			}
346 			int index = this.types.indexOf(typeToFind);
347 			if (index != -1) {
348 				return this.types.get(index);
349 			}
350 			return null;
351 		}
352 		@Override
hashCode()353 		public int hashCode() {
354 			final int prime = 31;
355 			int result = 1;
356 			result = prime * result + ((name == null) ? 0 : name.hashCode());
357 			return result;
358 		}
persistAsClassStubsForZip(ZipOutputStream zipOutputStream, ProfileInfo info)359 		public void persistAsClassStubsForZip(ZipOutputStream zipOutputStream, ProfileInfo info) throws IOException {
360 			if (this.types == null) {
361 				return;
362 			}
363 			Collections.sort(this.types);
364 			for (Type type : this.types) {
365 				StringBuilder buffer = new StringBuilder(this.name.replace('.', '/'));
366 				String simpleName = type.getSimpleName();
367 				buffer.append('/').append(simpleName.replace('.', '$'));
368 				byte[] classFileBytes = info.getClassFileBytes(type);
369 				if (classFileBytes != null) {
370 					Util.writeZipFileEntry(zipOutputStream, String.valueOf(buffer), classFileBytes);
371 				}
372 			}
373 		}
persistXML(Document document, Element element, String OSGiProfileName)374 		public void persistXML(Document document, Element element, String OSGiProfileName) {
375 			Element pkg = document.createElement(IApiXmlConstants.ELEMENT_PACKAGE);
376 			pkg.setAttribute(IApiXmlConstants.ATTR_NAME, this.name);
377 			element.appendChild(pkg);
378 			if (this.types == null) {
379 				return;
380 			}
381 			Collections.sort(this.types);
382 			for (Iterator<Type> iterator2 = this.types.iterator(); iterator2.hasNext();) {
383 				iterator2.next().persistXML(document, pkg, OSGiProfileName);
384 			}
385 		}
size()386 		public int size() {
387 			return this.types == null ? 0 : this.types.size();
388 		}
389 		@Override
toString()390 		public String toString() {
391 			StringBuilder builder = new StringBuilder();
392 			builder.append("Package : "); //$NON-NLS-1$
393 			builder.append(this.name).append(Util.LINE_SEPARATOR);
394 			return String.valueOf(builder);
395 		}
396 	}
397 
398 	static class ProfileInfo {
399 		private static final String BLACK_LIST_NAME = "_blackList_.txt"; //$NON-NLS-1$
400 		private static final String CDC_SUBDIR = "cdc"; //$NON-NLS-1$
401 		private static final String JRE_SUBDIR = "jre"; //$NON-NLS-1$
402 		private static final String OSGI_SUBDIR = "osgi"; //$NON-NLS-1$
403 		private static final String OTHER_PACKAGES = "org.osgi.framework.system.packages"; //$NON-NLS-1$
404 
checkDocStatus(ProfileInfo info, Type type, ZipFile docZip, String docURL, String docRoot)405 		private static boolean checkDocStatus(ProfileInfo info, Type type, ZipFile docZip, String docURL, String docRoot) {
406 			if (docZip == null && docURL == null) {
407 				// if no doc to validate we accept it if on white list
408 				if (DEBUG) {
409 					System.out.println("No javadoc zip or url for " + info.profileName); //$NON-NLS-1$
410 				}
411 				return info.isOnWhiteList(type);
412 			}
413 			String typeName = getDocTypeName(docRoot, type);
414 			if (DEBUG) {
415 				System.out.println("Retrieving javadoc for type: " + typeName); //$NON-NLS-1$
416 			}
417 			if (docZip == null) {
418 				char[] contents = info.getOnlineDocContents(docURL, typeName);
419 				if (contents == null) {
420 					if (DEBUG) {
421 						System.out.println("Found no doc for " + typeName + " - check whitelist"); //$NON-NLS-1$ //$NON-NLS-2$
422 					}
423 					return info.isOnWhiteList(type);
424 				}
425 				return true;
426 			}
427 			return docZip.getEntry(typeName) != null || info.isOnWhiteList(type);
428 		}
429 
getDocTypeName(String docRoot, Type type)430 		public static String getDocTypeName(String docRoot, Type type) {
431 			StringBuilder buffer = new StringBuilder(docRoot);
432 			char[] typeNameForDoc = CharOperation.replaceOnCopy(type.name, '.', '/');
433 			typeNameForDoc = CharOperation.replaceOnCopy(typeNameForDoc, '$', '.');
434 			buffer.append(typeNameForDoc);
435 			buffer.append(".html"); //$NON-NLS-1$
436 			return String.valueOf(buffer);
437 		}
438 
getProfileInfo(String profileName, String jreLib, String osgiProfile, String jreDoc, String jreURL, String docRoot, String cacheLocation, String whiteList)439 		public static ProfileInfo getProfileInfo(String profileName, String jreLib, String osgiProfile, String jreDoc, String jreURL, String docRoot, String cacheLocation, String whiteList) {
440 			ProfileInfo info = new ProfileInfo(profileName, jreLib, osgiProfile, jreDoc, jreURL, docRoot, cacheLocation, whiteList);
441 			if (info.getProfileName() == null) {
442 				return null;
443 			}
444 			return info;
445 		}
446 
initializePackages(String fileName)447 		private static Set<String> initializePackages(String fileName) {
448 			Set<String> knownPackages = null;
449 			try {
450 				Properties allProperties = new Properties();
451 				BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));
452 				allProperties.load(inputStream);
453 				inputStream.close();
454 				String property = allProperties.getProperty(OTHER_PACKAGES);
455 				if (property != null && property.length() != 0) {
456 					String[] packages = property.split(","); //$NON-NLS-1$
457 					for (String package1 : packages) {
458 						if (knownPackages == null) {
459 							knownPackages = new LinkedHashSet<>();
460 						}
461 						knownPackages.add(package1);
462 					}
463 				} else {
464 					knownPackages = Collections.emptySet();
465 				}
466 			} catch (IOException e) {
467 				// ignore
468 			}
469 			return knownPackages;
470 		}
initializeWhiteList(String fileName)471 		private static Set<String> initializeWhiteList(String fileName) {
472 			if (fileName == null) {
473 				return Collections.emptySet();
474 			}
475 			Set<String> values = new HashSet<>();
476 			try (LineNumberReader reader = new LineNumberReader(new BufferedReader(new FileReader(fileName)))) {
477 				String line;
478 				while ((line = reader.readLine()) != null) {
479 					String trimmedLine = line.trim();
480 					if (!trimmedLine.isEmpty()) {
481 						// only non-empty lines are added trimmed on both ends
482 						values.add(trimmedLine);
483 					}
484 				}
485 			} catch (IOException e) {
486 				// ignore
487 			}
488 			return values;
489 		}
490 
491 		File[] allFiles;
492 		// set of type names that don't have javadoc
493 		Set<String> blackList;
494 		String cacheLocation;
495 		Map<String, Package> data;
496 		String docRoot;
497 		long generatedSize;
498 		String JREdoc;
499 		String JRElib;
500 		String JREURL;
501 
502 		String OSGiProfile;
503 		String profileName;
504 		long totalSize;
505 
506 		Set<String> whiteList;
507 
ProfileInfo(String profilename, String jreLib, String osgiProfile, String jreDoc, String jreURL, String docroot, String cacheloc, String whitelist)508 		private ProfileInfo(String profilename, String jreLib, String osgiProfile, String jreDoc, String jreURL, String docroot, String cacheloc, String whitelist) {
509 			this.JREdoc = jreDoc;
510 			this.JREURL = jreURL;
511 			this.JRElib = jreLib;
512 			this.OSGiProfile = osgiProfile;
513 			this.docRoot = docroot;
514 			this.profileName = profilename;
515 			this.cacheLocation = cacheloc;
516 			this.whiteList = initializeWhiteList(whitelist);
517 		}
518 
addToBlackList(String typeName)519 		private void addToBlackList(String typeName) {
520 			if (this.blackList != null) {
521 				this.blackList.add(typeName);
522 				return;
523 			}
524 			this.blackList = new TreeSet<>();
525 			this.blackList.add(typeName);
526 		}
527 
dumpToCache(String typeName, char[] contents)528 		public void dumpToCache(String typeName, char[] contents) {
529 			if (!CACHE_ENABLED || this.cacheLocation == null) {
530 				return;
531 			}
532 			File cacheDir = new File(this.cacheLocation);
533 			if (!cacheDir.exists()) {
534 				if (!cacheDir.mkdirs()) {
535 					System.err.println("Cache creation failed for " + typeName + " for profile " + this.getProfileName()); //$NON-NLS-1$ //$NON-NLS-2$
536 					return;
537 				}
538 			}
539 			File profileCache = new File(cacheDir, getProfileFileName());
540 			if (!profileCache.exists()) {
541 				if (!profileCache.mkdirs()) {
542 					System.err.println("Cache creation failed for " + typeName + " for profile " + this.getProfileName()); //$NON-NLS-1$ //$NON-NLS-2$
543 					return;
544 				}
545 			}
546 			File docType = new File(profileCache, typeName);
547 			if (docType.exists()) {
548 				// already in the cache
549 				return;
550 			} else {
551 				File parentFile = docType.getParentFile();
552 				if (!parentFile.exists()) {
553 					if (!parentFile.mkdirs()) {
554 						System.err.println("Cache creation failed for " + typeName + " for profile " + this.getProfileName()); //$NON-NLS-1$ //$NON-NLS-2$
555 						return;
556 					}
557 				}
558 				try (BufferedWriter writer = new BufferedWriter(new FileWriter(docType))) {
559 					writer.write(contents);
560 					writer.flush();
561 				} catch (IOException e) {
562 					e.printStackTrace();
563 				}
564 			}
565 		}
566 
generateEEDescription(String outputDir)567 		void generateEEDescription(String outputDir) {
568 			if (this.data == null) {
569 				System.err.println("No data to persist for " + this.getProfileName()); //$NON-NLS-1$
570 				return;
571 			}
572 			String subDir = getSubDir();
573 			persistData(outputDir, subDir);
574 			persistDataAsClassFilesInZipFormat(outputDir, subDir);
575 		}
576 
getAllTypes()577 		public Map<String, Type> getAllTypes() {
578 			Map<String, Type> result = new LinkedHashMap<>();
579 			Set<String> keySet = this.data.keySet();
580 			String[] sortedKeys = new String[keySet.size()];
581 			keySet.toArray(sortedKeys);
582 			Arrays.sort(sortedKeys);
583 			for (String key : sortedKeys) {
584 				Package package1 = this.data.get(key);
585 				if (package1 != null) {
586 					package1.collectTypes(result);
587 				} else {
588 					System.err.println("Missing package for profile info XML serialization: " + key); //$NON-NLS-1$
589 				}
590 			}
591 			return result;
592 		}
593 
getCacheRoot()594 		private File getCacheRoot() {
595 			File cacheDir = new File(this.cacheLocation);
596 			if (cacheDir.exists() || cacheDir.mkdirs()) {
597 				File profileCache = new File(cacheDir, getProfileFileName());
598 				if (profileCache.exists() || profileCache.mkdirs()) {
599 					return profileCache;
600 				}
601 			}
602 			return null;
603 		}
604 
getClassFileBytes(Type type)605 		public byte[] getClassFileBytes(Type type) {
606 			if (this.allFiles == null) {
607 				throw new IllegalStateException("No jar files to open"); //$NON-NLS-1$
608 			}
609 			String typeName = new String(type.name);
610 			byte[] classFileBytes = null;
611 			try {
612 				String zipFileEntryName = typeName.replace('.', '/') + ".class"; //$NON-NLS-1$
613 				loop: for (int i = 0, max = this.allFiles.length; i < max; i++) {
614 					if (allFiles[i].toString().endsWith(".jmod") && !zipFileEntryName.startsWith("classes/")) { //$NON-NLS-1$ //$NON-NLS-2$
615 						zipFileEntryName = "classes/" + zipFileEntryName; //$NON-NLS-1$
616 					}
617 					try (ZipFile zipFile = new ZipFile(allFiles[i])) {
618 						ZipEntry zipEntry = zipFile.getEntry(zipFileEntryName);
619 						if (zipEntry == null) {
620 							continue loop;
621 						}
622 						try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry))) {
623 							classFileBytes = Util.getInputStreamAsByteArray(inputStream, -1);
624 							break loop;
625 						}
626 					}
627 				}
628 			} catch (ZipException e) {
629 				e.printStackTrace();
630 			} catch (IOException e) {
631 				e.printStackTrace();
632 			}
633 			if (classFileBytes == null) {
634 				throw new IllegalStateException("Could not retrieve byte[] for " + typeName); //$NON-NLS-1$
635 			}
636 			ClassReader classReader = new ClassReader(classFileBytes);
637 			StubClassAdapter visitor = new StubClassAdapter(type);
638 			classReader.accept(visitor, ClassReader.SKIP_DEBUG);
639 			if (visitor.shouldIgnore()) {
640 				return null;
641 			}
642 
643 			return visitor.getStub().getBytes();
644 		}
645 
getFromCache(String typeName)646 		public char[] getFromCache(String typeName) {
647 			if (!CACHE_ENABLED || this.cacheLocation == null) {
648 				return null;
649 			}
650 			File profileCache = getCacheRoot();
651 			if (profileCache != null) {
652 				File docType = new File(profileCache, typeName);
653 				if (docType.exists()) {
654 					try (BufferedInputStream stream = new BufferedInputStream(new FileInputStream(docType))) {
655 						return Util.getInputStreamAsCharArray(stream, -1, null);
656 					} catch (IOException e) {
657 						e.printStackTrace();
658 					}
659 				}
660 			}
661 			return null;
662 		}
663 
getOnlineDocContents(String docURL, String typeName)664 		char[] getOnlineDocContents(String docURL, String typeName) {
665 			// check the doc online
666 			String typeDocURL = docURL + typeName;
667 			char[] contents = this.getFromCache(typeName);
668 			if (contents == null && !ONLY_USE_CACHE) {
669 				// check the black list
670 				if (this.isOnBlackList(typeName)) {
671 					return null;
672 				}
673 				contents = Util.getURLContents(typeDocURL);
674 				if (contents == null) {
675 					this.addToBlackList(typeName);
676 					return null;
677 				} else {
678 					this.dumpToCache(typeName, contents);
679 				}
680 			}
681 			return contents;
682 		}
683 
getProfileFileName()684 		public String getProfileFileName() {
685 			return Util.getProfileFileName(getProfileName());
686 		}
687 
getProfileName()688 		public String getProfileName() {
689 			return this.profileName;
690 		}
691 
getSubDir()692 		private String getSubDir() {
693 			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)) {
694 				return CDC_SUBDIR;
695 			} 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)) {
696 				return OSGI_SUBDIR;
697 			} else {
698 				return JRE_SUBDIR;
699 			}
700 		}
701 
initializeBlackList()702 		private Set<String> initializeBlackList() {
703 			File cacheRoot = getCacheRoot();
704 			File blackListFile = new File(cacheRoot, BLACK_LIST_NAME);
705 			Set<String> values = new TreeSet<>();
706 			if (blackListFile.exists()) {
707 				try (LineNumberReader reader = new LineNumberReader(
708 						new BufferedReader(new FileReader(blackListFile)))) {
709 					String line;
710 					while ((line = reader.readLine()) != null) {
711 						String trimmedLine = line.trim();
712 						if (!trimmedLine.isEmpty()) {
713 							// only non-empty lines are added trimmed on both
714 							// ends
715 							values.add(trimmedLine);
716 						}
717 					}
718 				} catch (IOException e) {
719 					// ignore
720 				}
721 			}
722 			return values;
723 		}
724 
initializeData()725 		public void initializeData() throws IOException {
726 			String pname = this.getProfileName();
727 			if (pname == null) {
728 				// invalid profile info
729 				System.err.println("Info are invalid"); //$NON-NLS-1$
730 				return;
731 			}
732 
733 			if (DEBUG) {
734 				System.out.println("Profile : " + pname); //$NON-NLS-1$
735 			}
736 			long time = System.currentTimeMillis();
737 			this.allFiles = Util.getAllFiles(new File(this.JRElib),
738 					pathname -> pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".jar") //$NON-NLS-1$
739 							|| pathname.getName().toLowerCase().endsWith(".jmod")); //$NON-NLS-1$
740 			if (allFiles == null) {
741 				System.err.println("No jar files to proceed"); //$NON-NLS-1$
742 				return;
743 			}
744 			// initialize known packages
745 			String osgiProfileName = this.OSGiProfile;
746 			Set<String> knownPackages = initializePackages(osgiProfileName);
747 			// known packages should be part of the white list by default
748 			if (this.whiteList != null && !this.whiteList.isEmpty()) {
749 				this.whiteList.addAll(knownPackages);
750 			} else {
751 				this.whiteList = Collections.unmodifiableSet(knownPackages);
752 			}
753 			Map<String, Type> allVisibleTypes = new LinkedHashMap<>();
754 			Map<String, Type> allTypes = new LinkedHashMap<>();
755 			this.totalSize = 0;
756 			for (File currentFile : allFiles) {
757 				this.totalSize += currentFile.length();
758 
759 				try (ZipFile zipFile = new ZipFile(currentFile)) {
760 					for (Enumeration<? extends ZipEntry> enumeration = zipFile.entries(); enumeration.hasMoreElements();) {
761 						ZipEntry zipEntry = enumeration.nextElement();
762 						if (!zipEntry.getName().endsWith(".class")) //$NON-NLS-1$
763 						 {
764 							continue;
765 						}
766 						InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry));
767 						IClassFileReader classFileReader = null;
768 						try {
769 							classFileReader = ToolFactory.createDefaultClassFileReader(inputStream, IClassFileReader.ALL_BUT_METHOD_BODIES);
770 						} finally {
771 							inputStream.close();
772 						}
773 						if (classFileReader == null) {
774 							continue;
775 						}
776 						char[] className = classFileReader.getClassName();
777 						char[] packageName = null;
778 						int lastIndexOf = CharOperation.lastIndexOf('/', className);
779 						if (lastIndexOf == -1) {
780 							packageName = new char[0];
781 						} else {
782 							packageName = CharOperation.subarray(className, 0, lastIndexOf);
783 						}
784 						if (this.isMatching(knownPackages, packageName)) {
785 							Type type = Type.newType(this, classFileReader, this.JREdoc, this.JREURL, this.docRoot);
786 							if (type != null) {
787 								if (type.isProtected() || type.isPublic()) {
788 									allVisibleTypes.put(type.getFullQualifiedName(), type);
789 								}
790 								allTypes.put(type.getFullQualifiedName(), type);
791 							}
792 						}
793 					}
794 				}
795 			}
796 			// list all results
797 			List<Type> visibleTypes = new ArrayList<>();
798 			visibleTypes.addAll(allVisibleTypes.values());
799 			// check transitive closure to make sure all methods/fields from
800 			// superclass are visible when resolving fields/methods
801 			while (!visibleTypes.isEmpty()) {
802 				Type type = visibleTypes.remove(0);
803 				String superclassName = type.getSuperclassName();
804 				while (superclassName != null) {
805 					if (allVisibleTypes.get(superclassName) == null) {
806 						// look for required type
807 						Type currentSuperclass = allTypes.get(superclassName);
808 						if (currentSuperclass == null) {
809 							if (DEBUG) {
810 								System.out.println("Missing type: " + superclassName); //$NON-NLS-1$
811 							}
812 							break;
813 						} else {
814 							allVisibleTypes.put(currentSuperclass.getFullQualifiedName(), currentSuperclass);
815 							visibleTypes.add(currentSuperclass);
816 							if (DEBUG) {
817 								System.out.println("Reinject type: " + currentSuperclass.getFullQualifiedName()); //$NON-NLS-1$
818 							}
819 						}
820 					}
821 					Type superclass = allVisibleTypes.get(superclassName);
822 					superclassName = superclass.getSuperclassName();
823 				}
824 			}
825 			List<Type> isInDoc = new ArrayList<>();
826 			ZipFile docZip = null;
827 			if (this.JREdoc != null) {
828 				try {
829 					docZip = new ZipFile(this.JREdoc);
830 				} catch (FileNotFoundException e) {
831 					// no zip file
832 				}
833 			}
834 			try {
835 				for (Type type : allVisibleTypes.values()) {
836 					if (checkDocStatus(this, type, docZip, this.JREURL, this.docRoot)) {
837 						isInDoc.add(type);
838 					}
839 				}
840 			} finally {
841 				if (docZip != null) {
842 					docZip.close();
843 				}
844 			}
845 			HashMap<String, Package> typesPerPackage = new LinkedHashMap<>();
846 			for (Type type : isInDoc) {
847 				String packageName = type.getPackage();
848 				Package package1 = typesPerPackage.get(packageName);
849 				if (package1 == null) {
850 					package1 = new Package(packageName);
851 					typesPerPackage.put(packageName, package1);
852 				}
853 				package1.addType(type);
854 			}
855 			this.data = typesPerPackage;
856 			if (DEBUG) {
857 				System.out.println("Time spent for gathering datas for " + pname + " : " + (System.currentTimeMillis() - time) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
858 			}
859 
860 			if (this.blackList != null) {
861 				// persist black list
862 				this.persistBlackList();
863 			}
864 		}
865 
isMatching(Set<String> knownPackages, char[] packageName)866 		private boolean isMatching(Set<String> knownPackages, char[] packageName) {
867 			if (CharOperation.indexOf("java/".toCharArray(), packageName, true) == 0) { //$NON-NLS-1$
868 				return true;
869 			}
870 			if (knownPackages.isEmpty()) {
871 				return false;
872 			}
873 			String currentPackage = new String(CharOperation.replaceOnCopy(packageName, '/', '.'));
874 			return knownPackages.contains(currentPackage) || this.isOnWhiteList(currentPackage);
875 		}
876 
isOnBlackList(String typeName)877 		private boolean isOnBlackList(String typeName) {
878 			if (this.blackList != null) {
879 				return this.blackList.contains(typeName);
880 			}
881 			// retrieve black list if it exists
882 			this.blackList = initializeBlackList();
883 			return this.blackList.contains(typeName);
884 		}
isOnWhiteList(String packageName)885 		private boolean isOnWhiteList(String packageName) {
886 			return packageName.startsWith("java.") || packageName.startsWith("javax.") || this.whiteList.contains(packageName); //$NON-NLS-1$ //$NON-NLS-2$
887 		}
isOnWhiteList(Type type)888 		private boolean isOnWhiteList(Type type) {
889 			return isOnWhiteList(type.getPackage());
890 		}
persistBlackList()891 		private void persistBlackList() {
892 			if (this.blackList == null || this.blackList.isEmpty()) {
893 				return;
894 			}
895 			File cacheRoot = getCacheRoot();
896 			File blackListFile = new File(cacheRoot, BLACK_LIST_NAME);
897 			if (!blackListFile.exists()) {
898 				try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(blackListFile)));) {
899 					for (Iterator<String> iterator = this.blackList.iterator(); iterator.hasNext();) {
900 						writer.println(iterator.next());
901 					}
902 					writer.flush();
903 				} catch (IOException e) {
904 					// ignore
905 				}
906 			}
907 		}
908 
persistChildren(Document document, Element xmlElement, Map<String, Package> packageMap, String OSGiProfileName)909 		private void persistChildren(Document document, Element xmlElement, Map<String, Package> packageMap, String OSGiProfileName) {
910 			Set<String> keySet = packageMap.keySet();
911 			String[] sortedKeys = new String[keySet.size()];
912 			keySet.toArray(sortedKeys);
913 			Arrays.sort(sortedKeys);
914 			for (String key : sortedKeys) {
915 				Package package1 = packageMap.get(key);
916 				if (package1 != null) {
917 					package1.persistXML(document, xmlElement, OSGiProfileName);
918 				} else {
919 					System.err.println("Missing package for profile info XML serialization: " + key); //$NON-NLS-1$
920 				}
921 			}
922 		}
923 
persistChildrenAsClassFile(ZipOutputStream zipOutputStream, Map<String, Package> packageMap, ProfileInfo info)924 		private void persistChildrenAsClassFile(ZipOutputStream zipOutputStream, Map<String, Package> packageMap, ProfileInfo info) throws IOException {
925 			Set<String> keySet = packageMap.keySet();
926 			String[] sortedKeys = new String[keySet.size()];
927 			keySet.toArray(sortedKeys);
928 			Arrays.sort(sortedKeys);
929 			for (String key : sortedKeys) {
930 				Package package1 = packageMap.get(key);
931 				if (package1 != null) {
932 					package1.persistAsClassStubsForZip(zipOutputStream, info);
933 				} else {
934 					System.err.println("Missing package for profile info zip serialization: " + key); //$NON-NLS-1$
935 				}
936 			}
937 		}
persistData(String rootName, String subDirName)938 		private void persistData(String rootName, String subDirName) {
939 			try {
940 				Document document = org.eclipse.pde.api.tools.internal.util.Util.newDocument();
941 				Element component = document.createElement(IApiXmlConstants.ELEMENT_COMPONENT);
942 				String profileName2 = this.getProfileName();
943 				component.setAttribute(IApiXmlConstants.ATTR_ID, profileName2);
944 				component.setAttribute(IApiXmlConstants.ATTR_VERSION, IApiXmlConstants.API_DESCRIPTION_CURRENT_VERSION);
945 				document.appendChild(component);
946 				persistChildren(document, component, this.data, profileName2);
947 				String contents = org.eclipse.pde.api.tools.internal.util.Util.serializeDocument(document);
948 				String fileName = profileName2 + ".xml"; //$NON-NLS-1$
949 				Util.write(rootName, subDirName, fileName, contents);
950 			} catch (DOMException e) {
951 				e.printStackTrace();
952 			} catch (CoreException e) {
953 				e.printStackTrace();
954 			}
955 		}
956 
persistDataAsClassFilesInZipFormat(String rootName, String subDirName)957 		private void persistDataAsClassFilesInZipFormat(String rootName, String subDirName) {
958 			ZipOutputStream zipOutputStream = null;
959 			String profileName2 = this.getProfileFileName();
960 			File root = new File(rootName);
961 			if (!root.exists()) {
962 				root.mkdirs();
963 			}
964 			File subDir = new File(root, subDirName);
965 			if (!subDir.exists()) {
966 				subDir.mkdir();
967 			}
968 			if (profileName2.indexOf('/') != 0) {
969 				profileName2 = profileName2.replace('/', '_');
970 			}
971 			File file = new File(subDir, profileName2 + ".zip"); //$NON-NLS-1$
972 			try {
973 				zipOutputStream = Util.getOutputStream(file);
974 			} catch (FileNotFoundException e) {
975 				e.printStackTrace();
976 			}
977 			if (zipOutputStream == null) {
978 				System.err.println("Could not create the output file : " + file.getAbsolutePath()); //$NON-NLS-1$
979 				return;
980 			}
981 			try {
982 				persistChildrenAsClassFile(zipOutputStream, this.data, this);
983 				zipOutputStream.flush();
984 			} catch (IOException e) {
985 				e.printStackTrace();
986 			} finally {
987 				try {
988 					zipOutputStream.close();
989 				} catch (IOException e) {
990 					// ignore
991 				}
992 			}
993 			this.generatedSize = file.length();
994 			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$
995 			System.out.println("Its generated size is " + this.generatedSize + " bytes."); //$NON-NLS-1$ //$NON-NLS-2$
996 			System.out.println("Ratio : " + (((double) this.generatedSize / (double) this.totalSize) * 100.0) + "%"); //$NON-NLS-1$ //$NON-NLS-2$
997 		}
998 		@Override
toString()999 		public String toString() {
1000 			return this.getProfileName();
1001 		}
1002 	}
1003 
1004 	static class StubClass {
1005 		private static final int CURRENT_VERSION = 3;
1006 		int access;
1007 		int classNameIndex;
1008 		List<StubField> fields;
1009 
1010 		int[] interfacesIndexes;
1011 		List<StubMethod> methods;
1012 		Map<String, Integer> pool;
1013 		int poolIndex;
1014 		int superNameIndex;
1015 
StubClass(int acc, String className2, String superName2, String[] interfaces2)1016 		public StubClass(int acc, String className2, String superName2, String[] interfaces2) {
1017 			this.access = acc;
1018 			this.pool = new LinkedHashMap<>();
1019 			this.classNameIndex = getIndex(className2);
1020 			this.superNameIndex = superName2 != null ? getIndex(superName2) : -1;
1021 			if (interfaces2 != null) {
1022 				this.interfacesIndexes = new int[interfaces2.length];
1023 				for (int i = 0; i < interfaces2.length; i++) {
1024 					this.interfacesIndexes[i] = getIndex(interfaces2[i]);
1025 				}
1026 			}
1027 		}
1028 
addField(String fieldName)1029 		public void addField(String fieldName) {
1030 			if (this.fields == null) {
1031 				this.fields = new ArrayList<>();
1032 			}
1033 			this.fields.add(new StubField(getIndex(fieldName)));
1034 		}
1035 
addMethod(String methodName, String desc)1036 		public StubMethod addMethod(String methodName, String desc) {
1037 			if (this.methods == null) {
1038 				this.methods = new ArrayList<>();
1039 			}
1040 			StubMethod method = new StubMethod(getIndex(methodName), getIndex(desc));
1041 			this.methods.add(method);
1042 			return method;
1043 		}
1044 
getBytes()1045 		public byte[] getBytes() {
1046 			ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
1047 
1048 			try (DataOutputStream outputStream = new DataOutputStream(byteArrayOutputStream)) {
1049 				outputStream.writeShort(CURRENT_VERSION);
1050 				outputStream.writeShort(this.poolIndex);
1051 				for (Entry<String, Integer> next : this.pool.entrySet()) {
1052 					outputStream.writeUTF(next.getKey());
1053 					outputStream.writeShort(next.getValue().intValue());
1054 				}
1055 				outputStream.writeChar(this.access);
1056 				outputStream.writeShort(this.classNameIndex);
1057 				outputStream.writeShort(this.superNameIndex);
1058 				int length = this.interfacesIndexes != null ? this.interfacesIndexes.length : 0;
1059 				outputStream.writeShort(length);
1060 				for (int i = 0; i < length; i++) {
1061 					outputStream.writeShort(this.interfacesIndexes[i]);
1062 				}
1063 				int fieldsLength = this.fields == null ? 0 : this.fields.size();
1064 				outputStream.writeShort(fieldsLength);
1065 				for (int i = 0; i < fieldsLength; i++) {
1066 					outputStream.writeShort(this.fields.get(i).nameIndex);
1067 				}
1068 				int methodsLength = this.methods == null ? 0 : this.methods.size();
1069 				outputStream.writeShort(methodsLength);
1070 				for (int i = 0; i < methodsLength; i++) {
1071 					StubMethod stubMethod = this.methods.get(i);
1072 					outputStream.writeShort(stubMethod.selectorIndex);
1073 					outputStream.writeShort(stubMethod.signatureIndex);
1074 					outputStream.writeByte(stubMethod.isPolymorphic ? 1 : 0);
1075 				}
1076 				outputStream.flush();
1077 			} catch (IOException e) {
1078 				e.printStackTrace();
1079 			}
1080 			this.pool = null;
1081 			this.fields = null;
1082 			this.methods = null;
1083 			return byteArrayOutputStream.toByteArray();
1084 		}
1085 
getIndex(String name)1086 		private int getIndex(String name) {
1087 			Integer integer = this.pool.get(name);
1088 			if (integer != null) {
1089 				return integer.intValue();
1090 			}
1091 			int value = this.poolIndex++;
1092 			this.pool.put(name, Integer.valueOf(value));
1093 			return value;
1094 		}
1095 	}
1096 
1097 	/**
1098 	 * Class adapter
1099 	 */
1100 	static class StubClassAdapter extends ClassVisitor {
1101 		static final int IGNORE_CLASS_FILE = 0x100;
1102 
1103 		int flags;
1104 		String name;
1105 		StubClass stub;
1106 		Type type;
1107 
1108 		/**
1109 		 * Constructor
1110 		 * @param stubtype
1111 		 */
StubClassAdapter(Type stubtype)1112 		public StubClassAdapter(Type stubtype) {
1113 			super(Opcodes.ASM6, new ClassWriter(0));
1114 			this.type = stubtype;
1115 		}
1116 
getStub()1117 		public StubClass getStub() {
1118 			return this.stub;
1119 		}
1120 
1121 		/**
1122 		 * @return if this class file should be ignored or not
1123 		 */
shouldIgnore()1124 		public boolean shouldIgnore() {
1125 			return (this.flags & IGNORE_CLASS_FILE) != 0;
1126 		}
1127 
1128 		@Override
visit(int version, int access, String className, String signature, String superName, String[] interfaces)1129 		public void visit(int version, int access, String className, String signature, String superName, String[] interfaces) {
1130 			this.name = className;
1131 			this.stub = new StubClass(access, className, superName, interfaces);
1132 		}
1133 
1134 		@Override
visitAnnotation(String arg0, boolean arg1)1135 		public AnnotationVisitor visitAnnotation(String arg0, boolean arg1) {
1136 			return null;
1137 		}
1138 
1139 		@Override
visitAttribute(Attribute attr)1140 		public void visitAttribute(Attribute attr) {
1141 			if ("Synthetic".equals(attr.type)) { //$NON-NLS-1$
1142 				this.flags |= IGNORE_CLASS_FILE;
1143 			}
1144 		}
1145 
1146 		@Override
visitEnd()1147 		public void visitEnd() {
1148 		}
1149 
1150 		@Override
visitField(int access, String fieldName, String desc, String signature, Object value)1151 		public FieldVisitor visitField(int access, String fieldName, String desc, String signature, Object value) {
1152 			if (type.getField(fieldName) == null) {
1153 				return null;
1154 			}
1155 			this.stub.addField(fieldName);
1156 			return null;
1157 		}
1158 
1159 		@Override
visitInnerClass(String innerClassName, String outerName, String innerName, int access)1160 		public void visitInnerClass(String innerClassName, String outerName, String innerName, int access) {
1161 			if (this.name.equals(innerClassName) && (outerName == null)) {
1162 				// local class
1163 				this.flags |= IGNORE_CLASS_FILE;
1164 			}
1165 		}
1166 
1167 		@Override
visitMethod(int access, String methodName, String desc, String signature, String[] exceptions)1168 		public MethodVisitor visitMethod(int access, String methodName, String desc, String signature, String[] exceptions) {
1169 			if ("<clinit>".equals(methodName)) { //$NON-NLS-1$
1170 				return null;
1171 			}
1172 			if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
1173 				return null;
1174 			}
1175 			if (((access & Opcodes.ACC_BRIDGE) != 0) || ((access & Opcodes.ACC_SYNTHETIC) != 0)) {
1176 				return null;
1177 			}
1178 			if (this.type.getMethod(methodName, desc) == null) {
1179 				return null;
1180 			}
1181 			final StubMethod method = this.stub.addMethod(methodName, desc);
1182 			return new MethodVisitor(Opcodes.ASM6, super.visitMethod(access, methodName, desc, signature, exceptions)) {
1183 				@Override
1184 				public AnnotationVisitor visitAnnotation(String sig, boolean visible) {
1185 					if (visible && "Ljava/lang/invoke/MethodHandle$PolymorphicSignature;".equals(sig)) { //$NON-NLS-1$
1186 						method.isPolymorphic();
1187 					}
1188 					return super.visitAnnotation(sig, visible);
1189 				}
1190 			};
1191 		}
1192 		@Override
1193 		public void visitOuterClass(String arg0, String arg1, String arg2) {
1194 			// ignore
1195 		}
1196 		@Override
1197 		public void visitSource(String arg0, String arg1) {
1198 			// ignore
1199 		}
1200 	}
1201 
1202 	static class StubField {
1203 		int nameIndex;
1204 
1205 		public StubField(int index) {
1206 			this.nameIndex = index;
1207 		}
1208 	}
1209 
1210 	static class StubMethod {
1211 		boolean isPolymorphic;
1212 		int selectorIndex;
1213 		int signatureIndex;
1214 
1215 		public StubMethod(int selectorindex, int sigindex) {
1216 			this.selectorIndex = selectorindex;
1217 			this.signatureIndex = sigindex;
1218 		}
1219 
1220 		public void isPolymorphic() {
1221 			this.isPolymorphic = true;
1222 		}
1223 	}
1224 
1225 	static class Type extends AbstractNode implements Comparable<Type> {
1226 
1227 		static boolean isFinal(int accessFlags) {
1228 			return (accessFlags & Flags.AccFinal) != 0;
1229 		}
1230 		static boolean isPrivate(int accessFlags) {
1231 			return (accessFlags & Flags.AccPrivate) != 0;
1232 		}
1233 		static boolean isProtected(int accessFlags) {
1234 			return (accessFlags & Flags.AccProtected) != 0;
1235 		}
1236 		static boolean isPublic(int accessFlags) {
1237 			return (accessFlags & Flags.AccPublic) != 0;
1238 		}
1239 		static boolean isStatic(int accessFlags) {
1240 			return (accessFlags & Flags.AccStatic) != 0;
1241 		}
1242 
1243 		private static boolean isVisibleField(int typeAccessFlags, int fieldAccessFlags) {
1244 			if (isPublic(fieldAccessFlags)) {
1245 				return true;
1246 			}
1247 			if (isProtected(fieldAccessFlags)) {
1248 				return !isFinal(typeAccessFlags);
1249 			}
1250 			return false;
1251 		}
1252 
1253 		private static boolean isVisibleMethod(int typeAccessFlags, int methodAccessFlags) {
1254 			if (isPublic(methodAccessFlags)) {
1255 				return true;
1256 			}
1257 			if (isProtected(methodAccessFlags)) {
1258 				return !isFinal(typeAccessFlags);
1259 			}
1260 			return false;
1261 		}
1262 
1263 		public static Type newType(ProfileInfo info, IClassFileReader reader, String docZipFileName, String docURL, String docRoot) {
1264 			IInnerClassesAttribute innerClassesAttribute = reader.getInnerClassesAttribute();
1265 			if (innerClassesAttribute != null) {
1266 				// search the right entry
1267 				IInnerClassesAttributeEntry[] entries = innerClassesAttribute.getInnerClassAttributesEntries();
1268 				for (IInnerClassesAttributeEntry entry : entries) {
1269 					char[] innerClassName = entry.getInnerClassName();
1270 					if (innerClassName != null) {
1271 						if (CharOperation.equals(reader.getClassName(), innerClassName)) {
1272 							int accessFlags2 = entry.getAccessFlags();
1273 							if (isPrivate(accessFlags2)) {
1274 								return null;
1275 							}
1276 						}
1277 					}
1278 				}
1279 			}
1280 			return new Type(reader, docZipFileName);
1281 		}
1282 
1283 		Set<Field> fields;
1284 		Set<Method> methods;
1285 		int modifiers;
1286 
1287 		char[] name;
1288 		char[] superclassName;
1289 		char[][] superinterfacesNames;
1290 
1291 		private Type(IClassFileReader reader, String docZipFileName) {
1292 			ZipFile docZip = null;
1293 			try {
1294 				if (docZipFileName != null) {
1295 					try {
1296 						docZip = new ZipFile(docZipFileName);
1297 					} catch (FileNotFoundException e) {
1298 						// no zip file
1299 					}
1300 				}
1301 				char[] className = reader.getClassName();
1302 				className = CharOperation.replaceOnCopy(className, '/', '.');
1303 				this.name = className;
1304 				if (DEBUG) {
1305 					System.out.println("Adding type: " + String.valueOf(className)); //$NON-NLS-1$
1306 				}
1307 				char[] scname = reader.getSuperclassName();
1308 				if (scname != null) {
1309 					scname = CharOperation.replaceOnCopy(scname, '/', '.');
1310 					this.superclassName = scname;
1311 				}
1312 				char[][] interfaceNames = CharOperation.deepCopy(reader.getInterfaceNames());
1313 				for (char[] interfaceName : interfaceNames) {
1314 					CharOperation.replace(interfaceName, '/', '.');
1315 				}
1316 				this.superinterfacesNames = interfaceNames;
1317 				this.modifiers = reader.getAccessFlags();
1318 				IFieldInfo[] fieldInfos = reader.getFieldInfos();
1319 				int length = fieldInfos.length;
1320 				for (int i = 0; i < length; i++) {
1321 					IFieldInfo fieldInfo = fieldInfos[i];
1322 					if (isVisibleField(this.modifiers, fieldInfo.getAccessFlags())) {
1323 						if (fields == null) {
1324 							this.fields = new HashSet<>();
1325 						}
1326 						Field field = new Field(fieldInfo.getName(), fieldInfo.getDescriptor());
1327 						fields.add(field);
1328 						if (DEBUG) {
1329 							System.out.println("Adding field: " + field); //$NON-NLS-1$
1330 						}
1331 					}
1332 				}
1333 				IMethodInfo[] methodInfos = reader.getMethodInfos();
1334 				length = methodInfos.length;
1335 				for (IMethodInfo methodInfo : methodInfos) {
1336 					IClassFileAttribute[] attributes = methodInfo.getAttributes();
1337 					ISignatureAttribute signatureAttribute = null;
1338 					for (IClassFileAttribute currentAttribute : attributes) {
1339 						if (CharOperation.equals(currentAttribute.getAttributeName(), IAttributeNamesConstants.SIGNATURE)) {
1340 							signatureAttribute = (ISignatureAttribute) currentAttribute;
1341 							break;
1342 						}
1343 					}
1344 					char[] signature = null;
1345 					if (signatureAttribute != null) {
1346 						signature = signatureAttribute.getSignature();
1347 					} else {
1348 						signature = methodInfo.getDescriptor();
1349 					}
1350 					int accessFlags = methodInfo.getAccessFlags();
1351 					if (isVisibleMethod(this.modifiers, accessFlags)) {
1352 						if (methods == null) {
1353 							this.methods = new HashSet<>();
1354 						}
1355 						Method method = new Method(accessFlags, methodInfo.getName(), methodInfo.getDescriptor(), signatureAttribute == null ? null : signature);
1356 						methods.add(method);
1357 						if (DEBUG) {
1358 							System.out.println("Adding method: " + method); //$NON-NLS-1$
1359 						}
1360 					}
1361 				}
1362 			} catch (IOException e) {
1363 				// no zip file
1364 				System.err.println("Missing doc zip at " + docZipFileName); //$NON-NLS-1$
1365 			} finally {
1366 				if (docZip != null) {
1367 					try {
1368 						docZip.close();
1369 					} catch (IOException e) {
1370 						// ignore
1371 					}
1372 				}
1373 			}
1374 		}
1375 
1376 		public void addField(Field f) {
1377 			if (this.fields == null) {
1378 				this.fields = new HashSet<>();
1379 			}
1380 			this.fields.add(f);
1381 		}
1382 		public void addMethod(Method m) {
1383 			if (this.methods == null) {
1384 				this.methods = new HashSet<>();
1385 			}
1386 			this.methods.add(m);
1387 		}
1388 		@Override
1389 		public int compareTo(Type type) {
1390 			return this.getSimpleName().compareTo(type.getSimpleName());
1391 		}
1392 
1393 		@Override
1394 		public boolean equals(Object obj) {
1395 			if (this == obj) {
1396 				return true;
1397 			}
1398 			if (obj == null) {
1399 				return false;
1400 			}
1401 			if (!(obj instanceof Type)) {
1402 				return false;
1403 			}
1404 			Type other = (Type) obj;
1405 			return Arrays.equals(name, other.name);
1406 		}
1407 		public Field getField(String fname) {
1408 			if (this.fields == null) {
1409 				return null;
1410 			}
1411 			Field fieldToFind = new Field(fname.toCharArray(), null);
1412 			for (Field currentField : this.fields) {
1413 				if (fieldToFind.equals(currentField)) {
1414 					return currentField;
1415 				}
1416 			}
1417 			return null;
1418 		}
1419 
1420 		public String getFullQualifiedName() {
1421 			return String.valueOf(this.name);
1422 		}
1423 		public String getSuperclassName() {
1424 			if (this.superclassName == null) {
1425 				return null;
1426 			}
1427 			return String.valueOf(this.superclassName);
1428 		}
1429 		public Method getMethod(String selector, String signature) {
1430 			if (this.methods == null) {
1431 				return null;
1432 			}
1433 			Method methodToFind = new Method(0, selector.toCharArray(), signature.toCharArray(), null);
1434 			for (Method currentMethod : this.methods) {
1435 				if (methodToFind.equals(currentMethod)) {
1436 					return currentMethod;
1437 				}
1438 			}
1439 			return null;
1440 		}
1441 		public String getPackage() {
1442 			int index = CharOperation.lastIndexOf('.', this.name);
1443 			return new String(CharOperation.subarray(this.name, 0, index));
1444 		}
1445 		public String getSimpleName() {
1446 			return Util.getSimpleName(this.name);
1447 		}
1448 
1449 		@Override
1450 		public int hashCode() {
1451 			final int prime = 31;
1452 			int result = 1;
1453 			result = prime * result + Arrays.hashCode(name);
1454 			return result;
1455 		}
1456 
1457 		public boolean isProtected() {
1458 			return isProtected(this.modifiers);
1459 		}
1460 
1461 		public boolean isPublic() {
1462 			return isPublic(this.modifiers);
1463 		}
1464 
1465 		public void persistXML(Document document, Element parent, String OSGiProfileName) {
1466 			Element type = document.createElement(IApiXmlConstants.ELEMENT_TYPE);
1467 			parent.appendChild(type);
1468 			type.setAttribute(IApiXmlConstants.ATTR_NAME, getSimpleName());
1469 			if (this.superclassName != null) {
1470 				type.setAttribute(IApiXmlConstants.ATTR_SUPER_CLASS, new String(this.superclassName));
1471 			}
1472 			if (this.superinterfacesNames != null && this.superinterfacesNames.length != 0) {
1473 				type.setAttribute(IApiXmlConstants.ATTR_SUPER_INTERFACES, Util.getInterfaces(this.superinterfacesNames));
1474 			}
1475 			type.setAttribute(IApiXmlConstants.ATTR_INTERFACE, Boolean.toString((this.modifiers & Flags.AccInterface) != 0));
1476 			persistAnnotations(type, OSGiProfileName);
1477 			if (this.fields != null) {
1478 				Field[] allFields = new Field[this.fields.size()];
1479 				this.fields.toArray(allFields);
1480 				Arrays.sort(allFields);
1481 				for (Field allField : allFields) {
1482 					allField.persistXML(document, type, OSGiProfileName);
1483 				}
1484 			}
1485 			if (this.methods != null) {
1486 				Method[] allMethods = new Method[this.methods.size()];
1487 				this.methods.toArray(allMethods);
1488 				Arrays.sort(allMethods);
1489 				for (Method allMethod : allMethods) {
1490 					allMethod.persistXML(document, type, OSGiProfileName);
1491 				}
1492 			}
1493 		}
1494 		@Override
1495 		public String toString() {
1496 			StringBuilder buffer = new StringBuilder();
1497 			buffer.append(this.getPackage() + "." + this.getSimpleName()).append(Util.LINE_SEPARATOR); //$NON-NLS-1$
1498 			// list all fields
1499 
1500 			if (this.fields != null) {
1501 				Field[] allFields = new Field[this.fields.size()];
1502 				this.fields.toArray(allFields);
1503 				Arrays.sort(allFields);
1504 				for (Field field : allFields) {
1505 					buffer.append("\t") //$NON-NLS-1$
1506 					.append(field).append(Util.LINE_SEPARATOR);
1507 				}
1508 			}
1509 			if (this.methods != null) {
1510 				Method[] allMethods = new Method[this.methods.size()];
1511 				this.methods.toArray(allMethods);
1512 				Arrays.sort(allMethods);
1513 				for (Method method : allMethods) {
1514 					buffer.append("\t").append(method).append(Util.LINE_SEPARATOR); //$NON-NLS-1$
1515 				}
1516 			}
1517 			return String.valueOf(buffer);
1518 		}
1519 	}
1520 	static SortedSet<String> ACCEPTED_EEs;
1521 	static boolean CACHE_ENABLED = true;
1522 	static boolean DEBUG = false;
1523 	static boolean ONLY_USE_CACHE = false;
1524 	static final String PROPERTY_CACHE_LOCATION = ".cacheLocation"; //$NON-NLS-1$
1525 	static final String PROPERTY_DOC_ROOT = ".docRoot"; //$NON-NLS-1$
1526 	static final String PROPERTY_JRE_DOC = ".jreDoc"; //$NON-NLS-1$
1527 	static final String PROPERTY_JRE_LIB = ".jreLib"; //$NON-NLS-1$
1528 	static final String PROPERTY_JRE_URL = ".jreURL"; //$NON-NLS-1$
1529 	static final String PROPERTY_OSGI_PROFILE = ".osgiProfile"; //$NON-NLS-1$
1530 	static final String PROPERTY_WHITE_LIST = ".whiteList"; //$NON-NLS-1$
1531 
1532 	static {
1533 		String[] ees = new String[] { "JRE-1.1", //$NON-NLS-1$
1534 				"J2SE-1.2", //$NON-NLS-1$
1535 				"J2SE-1.3", //$NON-NLS-1$
1536 				"J2SE-1.4", //$NON-NLS-1$
1537 				"J2SE-1.5", //$NON-NLS-1$
1538 				"JavaSE-1.6", //$NON-NLS-1$
1539 				"JavaSE-1.7", //$NON-NLS-1$
1540 				"JavaSE-1.8", //$NON-NLS-1$
1541 				"JavaSE-9", //$NON-NLS-1$ ,
1542 				"CDC-1.0_Foundation-1.0", //$NON-NLS-1$
1543 				"CDC-1.1_Foundation-1.1", //$NON-NLS-1$
1544 				"OSGi_Minimum-1.0", //$NON-NLS-1$
1545 				"OSGi_Minimum-1.1", //$NON-NLS-1$
1546 				"OSGi_Minimum-1.2" //$NON-NLS-1$
1547 		};
1548 		ACCEPTED_EEs = new TreeSet<>();
1549 		for (String ee : ees) {
1550 			ACCEPTED_EEs.add(ee);
1551 		}
1552 	}
1553 	private static String getAllEEValues() {
1554 		StringBuilder buffer = new StringBuilder();
1555 		for (String ee : ACCEPTED_EEs) {
1556 			if (buffer.length() != 0) {
1557 				buffer.append(", "); //$NON-NLS-1$
1558 			}
1559 			buffer.append(ee);
1560 		}
1561 		return String.valueOf(buffer);
1562 	}
1563 
1564 	public static void main(String[] args) {
1565 		EEGenerator generator = new EEGenerator();
1566 		generator.configure(args);
1567 		if (!generator.isInitialized()) {
1568 			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$
1569 			return;
1570 		}
1571 		String property = System.getProperty("DEBUG"); //$NON-NLS-1$
1572 		DEBUG = (property != null) && "true".equalsIgnoreCase(property); //$NON-NLS-1$
1573 		generator.run();
1574 	}
1575 	private ProfileInfo[] allProfiles;
1576 	String configurationFile;
1577 
1578 	String[] EEToGenerate;
1579 
1580 	String output;
1581 
1582 	private boolean checkFileProperty(String property) {
1583 		if (property == null) {
1584 			return false;
1585 		}
1586 		File jreDocFile = new File(property);
1587 		return jreDocFile.exists() && jreDocFile.isFile();
1588 	}
1589 
1590 	private boolean checkJREProperty(String property) {
1591 		if (property == null) {
1592 			return false;
1593 		}
1594 		File jreLibFolder = new File(property);
1595 		return jreLibFolder.exists() && jreLibFolder.isDirectory();
1596 	}
1597 
1598 	private void configure(String[] args) {
1599 		String currentArg = null;
1600 		int argCount = args.length;
1601 		int index = -1;
1602 		final int DEFAULT = 0;
1603 		final int OUTPUT = 1;
1604 		final int CONFIG = 2;
1605 		final int EEs = 3;
1606 		int mode = DEFAULT;
1607 		while (++index < argCount) {
1608 			currentArg = args[index];
1609 			switch (mode) {
1610 				case DEFAULT:
1611 					if ("-output".equals(currentArg)) { //$NON-NLS-1$
1612 						if (this.output != null)
1613 						 {
1614 							throw new IllegalArgumentException("output value is already set"); //$NON-NLS-1$
1615 						}
1616 						mode = OUTPUT;
1617 						continue;
1618 					}
1619 					if ("-config".equals(currentArg)) { //$NON-NLS-1$
1620 						if (this.configurationFile != null)
1621 						 {
1622 							throw new IllegalArgumentException("configuration value is already set"); //$NON-NLS-1$
1623 						}
1624 						mode = CONFIG;
1625 						continue;
1626 					}
1627 					if ("-EEs".equals(currentArg)) { //$NON-NLS-1$
1628 						if (this.EEToGenerate != null)
1629 						 {
1630 							throw new IllegalArgumentException("EEs value is already set"); //$NON-NLS-1$
1631 						}
1632 						mode = EEs;
1633 						continue;
1634 					}
1635 					// ignore unknown arguments - might be passed in by the
1636 					// Eclipse application
1637 					continue;
1638 				case OUTPUT:
1639 					this.output = currentArg;
1640 					mode = DEFAULT;
1641 					continue;
1642 				case CONFIG:
1643 					this.configurationFile = currentArg;
1644 					mode = DEFAULT;
1645 					continue;
1646 				case EEs:
1647 					String listOfEEs = currentArg;
1648 					StringTokenizer tokenizer = new StringTokenizer(listOfEEs, ","); //$NON-NLS-1$
1649 				List<String> list = new ArrayList<>();
1650 					while (tokenizer.hasMoreTokens()) {
1651 						String currentEE = tokenizer.nextToken().trim();
1652 						if (ACCEPTED_EEs.contains(currentEE)) {
1653 							list.add(currentEE);
1654 						} else {
1655 							throw new IllegalArgumentException("Wrong EE value: " + currentEE + " accepted values are: " + getAllEEValues()); //$NON-NLS-1$ //$NON-NLS-2$
1656 						}
1657 					}
1658 					if (!list.isEmpty()) {
1659 						list.toArray(this.EEToGenerate = new String[list.size()]);
1660 					}
1661 					mode = DEFAULT;
1662 					continue;
1663 				default:
1664 					break;
1665 			}
1666 		}
1667 		if (this.output == null) {
1668 			throw new IllegalArgumentException("output value is missing"); //$NON-NLS-1$
1669 		}
1670 		// check output
1671 		File file = new File(this.output);
1672 		if (!file.exists()) {
1673 			if (!file.mkdirs()) {
1674 				this.output = null;
1675 				throw new IllegalArgumentException("Could not create the output dir"); //$NON-NLS-1$
1676 			}
1677 		}
1678 		// check configuration file
1679 		File configuration = new File(this.configurationFile);
1680 		if (!configuration.exists()) {
1681 			this.configurationFile = null;
1682 			throw new IllegalArgumentException("Configuration file doesn't exist"); //$NON-NLS-1$
1683 		}
1684 		Properties properties = new Properties();
1685 		BufferedReader reader = null;
1686 		try {
1687 			reader = new BufferedReader(new FileReader(configuration));
1688 			properties.load(reader);
1689 		} catch (IOException e) {
1690 			e.printStackTrace();
1691 			throw new IllegalArgumentException("Could not properly initialize the properties"); //$NON-NLS-1$
1692 		} finally {
1693 			if (reader != null) {
1694 				try {
1695 					reader.close();
1696 				} catch (IOException e) {
1697 					// ignore
1698 				}
1699 			}
1700 		}
1701 		List<ProfileInfo> infos = new ArrayList<>();
1702 		for (String EE : EEToGenerate) {
1703 			// Retrieve all properties for each EE
1704 			// JRELIB, OSGI_PROFILE, JRE_DOC, JRE_URL, CACHE, WHITE_LIST
1705 			String key = EE + PROPERTY_JRE_LIB;
1706 			String jreLibProperty = properties.getProperty(key, null);
1707 			if (!checkJREProperty(jreLibProperty)) {
1708 				throw new IllegalArgumentException("Wrong property value : " + key); //$NON-NLS-1$
1709 			}
1710 			key = EE + PROPERTY_CACHE_LOCATION;
1711 			String cacheLocationProperty = properties.getProperty(key, null);
1712 			if (cacheLocationProperty != null) {
1713 				if (cacheLocationProperty.isEmpty()) {
1714 					cacheLocationProperty = null;
1715 				}
1716 			}
1717 			key = EE + PROPERTY_DOC_ROOT;
1718 			String docRootProperty = properties.getProperty(key, ""); //$NON-NLS-1$
1719 			key = EE + PROPERTY_JRE_DOC;
1720 			String jreDocProperty = properties.getProperty(key, null);
1721 			if (jreDocProperty != null && !jreDocProperty.isEmpty()) {
1722 				if (!checkFileProperty(jreDocProperty)) {
1723 					throw new IllegalArgumentException("Wrong property value : " + key); //$NON-NLS-1$
1724 				}
1725 			} else {
1726 				jreDocProperty = null;
1727 			}
1728 			key = EE + PROPERTY_JRE_URL;
1729 			String jreUrlProperty = properties.getProperty(key, null);
1730 			if (jreUrlProperty != null && !jreUrlProperty.isEmpty()) {
1731 				if (Util.getURLContents(jreUrlProperty + docRootProperty + "java/lang/Object.html") == null) { //$NON-NLS-1$
1732 					throw new IllegalArgumentException("Wrong property value : " + key); //$NON-NLS-1$
1733 				}
1734 			} else {
1735 				jreUrlProperty = null;
1736 			}
1737 			key = EE + PROPERTY_OSGI_PROFILE;
1738 			String osgiProfileProperty = properties.getProperty(key, null);
1739 			if (osgiProfileProperty != null && !osgiProfileProperty.isEmpty()) {
1740 				if (!checkFileProperty(osgiProfileProperty)) {
1741 					throw new IllegalArgumentException("Wrong property value : " + key); //$NON-NLS-1$
1742 				}
1743 			} else {
1744 				osgiProfileProperty = null;
1745 			}
1746 			key = EE + PROPERTY_WHITE_LIST;
1747 			String whiteListProperty = properties.getProperty(key, null);
1748 			if (whiteListProperty != null && !whiteListProperty.isEmpty()) {
1749 				if (!checkFileProperty(whiteListProperty)) {
1750 					throw new IllegalArgumentException("Wrong property value : " + key); //$NON-NLS-1$
1751 				}
1752 			} else {
1753 				whiteListProperty = null;
1754 			}
1755 			infos.add(ProfileInfo.getProfileInfo(EE, jreLibProperty, osgiProfileProperty, jreDocProperty, jreUrlProperty, docRootProperty, cacheLocationProperty, whiteListProperty));
1756 		}
1757 		if (infos.isEmpty()) {
1758 			throw new IllegalArgumentException("Profile infos cannot be empty"); //$NON-NLS-1$
1759 		}
1760 		infos.toArray(allProfiles = new ProfileInfo[infos.size()]);
1761 	}
1762 
1763 	private boolean isInitialized() {
1764 		return this.configurationFile != null && this.EEToGenerate != null && this.output != null;
1765 	}
1766 
1767 	private void run() {
1768 		if (allProfiles == null) {
1769 			System.err.println("No descriptions to generate"); //$NON-NLS-1$
1770 			return;
1771 		}
1772 		int numberOfProfiles = allProfiles.length;
1773 		for (int i = 0; i < numberOfProfiles; i++) {
1774 			ProfileInfo profileInfo = allProfiles[i];
1775 			if (profileInfo != null) {
1776 				try {
1777 					profileInfo.initializeData();
1778 				} catch (IOException e) {
1779 					e.printStackTrace();
1780 				}
1781 				// persist the EE description
1782 				profileInfo.generateEEDescription(this.output);
1783 			}
1784 		}
1785 	}
1786 }
1787