1 /*******************************************************************************
2  *  Copyright (c) 2005, 2019 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.jdt.internal.launching.environments;
15 
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.net.URL;
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.LinkedHashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Properties;
27 import java.util.Set;
28 
29 import org.eclipse.core.resources.ResourcesPlugin;
30 import org.eclipse.core.runtime.FileLocator;
31 import org.eclipse.core.runtime.IConfigurationElement;
32 import org.eclipse.core.runtime.IPath;
33 import org.eclipse.core.runtime.NullProgressMonitor;
34 import org.eclipse.core.runtime.Path;
35 import org.eclipse.core.runtime.Platform;
36 import org.eclipse.jdt.core.IAccessRule;
37 import org.eclipse.jdt.core.IClasspathContainer;
38 import org.eclipse.jdt.core.IClasspathEntry;
39 import org.eclipse.jdt.core.IJavaModel;
40 import org.eclipse.jdt.core.IJavaProject;
41 import org.eclipse.jdt.core.JavaCore;
42 import org.eclipse.jdt.core.JavaModelException;
43 import org.eclipse.jdt.internal.launching.LaunchingPlugin;
44 import org.eclipse.jdt.launching.IVMInstall;
45 import org.eclipse.jdt.launching.IVMInstallChangedListener;
46 import org.eclipse.jdt.launching.JavaRuntime;
47 import org.eclipse.jdt.launching.LibraryLocation;
48 import org.eclipse.jdt.launching.PropertyChangeEvent;
49 import org.eclipse.jdt.launching.environments.IAccessRuleParticipant;
50 import org.eclipse.jdt.launching.environments.IExecutionEnvironment;
51 import org.eclipse.osgi.util.NLS;
52 import org.osgi.framework.Bundle;
53 import org.osgi.framework.Constants;
54 import org.osgi.framework.Version;
55 
56 /**
57  * A contributed execution environment.
58  *
59  * @since 3.2
60  */
61 class ExecutionEnvironment implements IExecutionEnvironment {
62 
63 	/**
64 	 * Add a VM changed listener to clear cached values when a VM changes or is removed
65 	 */
66 	private IVMInstallChangedListener fListener = new IVMInstallChangedListener() {
67 
68 		/* (non-Javadoc)
69 		 * @see org.eclipse.jdt.launching.IVMInstallChangedListener#defaultVMInstallChanged(org.eclipse.jdt.launching.IVMInstall, org.eclipse.jdt.launching.IVMInstall)
70 		 */
71 		@Override
72 		public void defaultVMInstallChanged(IVMInstall previous, IVMInstall current) {}
73 
74 		/* (non-Javadoc)
75 		 * @see org.eclipse.jdt.launching.IVMInstallChangedListener#vmAdded(org.eclipse.jdt.launching.IVMInstall)
76 		 */
77 		@Override
78 		public void vmAdded(IVMInstall newVm) {}
79 
80 		/* (non-Javadoc)
81 		 * @see org.eclipse.jdt.launching.IVMInstallChangedListener#vmChanged(org.eclipse.jdt.launching.PropertyChangeEvent)
82 		 */
83 		@Override
84 		public void vmChanged(PropertyChangeEvent event) {
85 			if (event.getSource() != null) {
86 				fParticipantMap.remove(event.getSource());
87 				fRuleCache.remove(event.getSource());
88 			}
89 		}
90 
91 		/* (non-Javadoc)
92 		 * @see org.eclipse.jdt.launching.IVMInstallChangedListener#vmRemoved(org.eclipse.jdt.launching.IVMInstall)
93 		 */
94 		@Override
95 		public void vmRemoved(IVMInstall removedVm) {
96 			fParticipantMap.remove(removedVm);
97 			fRuleCache.remove(removedVm);
98 		}
99 	};
100 
101 
102 	/**
103 	 * The backing <code>IConfigurationElement</code>
104 	 */
105 	private IConfigurationElement fElement;
106 
107 	/**
108 	 * Environment specific rule participant or <code>null</code> if none.
109 	 */
110 	private IAccessRuleParticipant fRuleParticipant;
111 
112 	/**
113 	 * OSGi profile properties or <code>null</code> if none.
114 	 */
115 	private Properties fProfileProperties;
116 
117 	/**
118 	 * Whether profile properties have been initialized
119 	 */
120 	private boolean fPropertiesInitialized;
121 
122 	/**
123 	 * Set of compatible vm's - just the strictly compatible ones
124 	 */
125 	private Set<IVMInstall> fStrictlyCompatible = new HashSet<>();
126 
127 	/**
128 	 * All compatible vm's
129 	 */
130 	private List<IVMInstall> fCompatibleVMs = new ArrayList<>();
131 
132 	/**
133 	 * default VM install or <code>null</code> if none
134 	 */
135 	private IVMInstall fDefault = null;
136 
137 	/**
138 	 * Cache of access rule participants to consider for this environment.
139 	 */
140 	private IAccessRuleParticipant[] fParticipants = null;
141 
142 	/**
143 	 * Map of {IVMInstall -> Map of {participant -> IAccessRule[][]}}.
144 	 * Caches access rules returned by each participant for a given VM.
145 	 * @since 3.3
146 	 */
147 	private Map<IVMInstall, Map<IAccessRuleParticipant, IAccessRule[][]>> fParticipantMap = new HashMap<>();
148 
149 	/**
150 	 * Cache of VM -> IAccessRule[][] based on the current state of the participant
151 	 * map. These are the union of the latest rules generated by the participants
152 	 * for a specific VM.
153 	 * @since 3.3
154 	 */
155 	private Map<IVMInstall, IAccessRule[][]> fRuleCache = new HashMap<>();
156 
157 	/**
158 	 * Wild card pattern matching all files
159 	 */
160 	private static final IPath ALL_PATTERN = new Path("**/*"); //$NON-NLS-1$
161 
162 	/**
163 	 * Prefix of compiler settings in properties file
164 	 */
165 	private static final String COMPILER_SETTING_PREFIX = JavaCore.PLUGIN_ID + ".compiler"; //$NON-NLS-1$
166 
167 	/**
168 	 * Constructor
169 	 * @param element the backing {@link IConfigurationElement}
170 	 */
ExecutionEnvironment(IConfigurationElement element)171 	ExecutionEnvironment(IConfigurationElement element) {
172 		fElement = element;
173 		fPropertiesInitialized = false;
174 		String attribute = fElement.getAttribute(EnvironmentsManager.RULE_PARTICIPANT_ELEMENT);
175 		if (attribute != null) {
176 			fRuleParticipant = new AccessRuleParticipant(fElement);
177 		}
178 		JavaRuntime.addVMInstallChangedListener(fListener);
179 	}
180 
181 	/**
182 	 * Initializes the <code>EnvironmentsManager</code>
183 	 */
init()184 	private void init() {
185 		EnvironmentsManager.getDefault().initializeCompatibilities();
186 	}
187 
188 	/* (non-Javadoc)
189 	 * @see org.eclipse.jdt.launching.environments.IExecutionEnvironment#getId()
190 	 */
191 	@Override
getId()192 	public String getId() {
193 		return fElement.getAttribute("id"); //$NON-NLS-1$
194 	}
195 
196 	/* (non-Javadoc)
197 	 * @see org.eclipse.jdt.launching.environments.IExecutionEnvironment#getDescription()
198 	 */
199 	@Override
getDescription()200 	public String getDescription() {
201 		return fElement.getAttribute("description"); //$NON-NLS-1$
202 	}
203 
204 	/* (non-Javadoc)
205 	 * @see org.eclipse.jdt.launching.environments.IExecutionEnvironment#getCompatibleVMs()
206 	 */
207 	@Override
getCompatibleVMs()208 	public IVMInstall[] getCompatibleVMs() {
209 		init();
210 		return fCompatibleVMs.toArray(new IVMInstall[fCompatibleVMs.size()]);
211 	}
212 
213 	/* (non-Javadoc)
214 	 * @see org.eclipse.jdt.launching.environments.IExecutionEnvironment#isStrictlyCompatible(org.eclipse.jdt.launching.IVMInstall)
215 	 */
216 	@Override
isStrictlyCompatible(IVMInstall vm)217 	public boolean isStrictlyCompatible(IVMInstall vm) {
218 		init();
219 		return fStrictlyCompatible.contains(vm);
220 	}
221 
222 	/* (non-Javadoc)
223 	 * @see org.eclipse.jdt.launching.environments.IExecutionEnvironment#getDefaultVM()
224 	 */
225 	@Override
getDefaultVM()226 	public IVMInstall getDefaultVM() {
227 		init();
228 		return fDefault;
229 	}
230 
231 	/* (non-Javadoc)
232 	 * @see org.eclipse.jdt.launching.environments.IExecutionEnvironment#setDefaultVM(org.eclipse.jdt.launching.IVMInstall)
233 	 */
234 	@Override
setDefaultVM(IVMInstall vm)235 	public void setDefaultVM(IVMInstall vm) {
236 		init();
237 		if (vm != null && !fCompatibleVMs.contains(vm)) {
238 			throw new IllegalArgumentException(NLS.bind(EnvironmentMessages.EnvironmentsManager_0, new String[]{getId()}));
239 		}
240 		if (vm != null && vm.equals(fDefault)) {
241 			return;
242 		}
243 		fDefault = vm;
244 		EnvironmentsManager.getDefault().updateDefaultVMs();
245 		// update classpath containers
246 		rebindClasspathContainers();
247 	}
248 
249 	/**
250 	 * Updates Java projects referencing this environment, if any.
251 	 */
rebindClasspathContainers()252 	private void rebindClasspathContainers() {
253 		IJavaModel model = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot());
254 		if (model != null) {
255 			try {
256 				List<IJavaProject> updates = new ArrayList<>();
257 				IJavaProject[] javaProjects = model.getJavaProjects();
258 				IPath path = JavaRuntime.newJREContainerPath(this);
259 				for (int i = 0; i < javaProjects.length; i++) {
260 					IJavaProject project = javaProjects[i];
261 					IClasspathEntry[] rawClasspath = project.getRawClasspath();
262 					for (int j = 0; j < rawClasspath.length; j++) {
263 						IClasspathEntry entry = rawClasspath[j];
264 						if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
265 							if (entry.getPath().equals(path)) {
266 								updates.add(project);
267 							}
268 						}
269 					}
270 				}
271 				if (!updates.isEmpty()) {
272 					JavaCore.setClasspathContainer(path,
273 							updates.toArray(new IJavaProject[updates.size()]),
274 							new IClasspathContainer[updates.size()],
275 							new NullProgressMonitor());
276 				}
277 			} catch (JavaModelException e) {
278 				LaunchingPlugin.log(e);
279 			}
280 		}
281 	}
282 
283 	/**
284 	 * Adds the specified VM to the listing of compatible VMs, also
285 	 * adds the VM to the listing of strictly compatible ones based on
286 	 * the strictlyCompatible flag
287 	 * @param vm the VM to add to the environment
288 	 * @param strictlyCompatible if it is strictly compatible
289 	 */
add(IVMInstall vm, boolean strictlyCompatible)290 	void add(IVMInstall vm, boolean strictlyCompatible) {
291 		if (fCompatibleVMs.contains(vm)) {
292 			return;
293 		}
294 		fCompatibleVMs.add(vm);
295 		if (strictlyCompatible) {
296 			fStrictlyCompatible.add(vm);
297 		}
298 	}
299 
300 	/**
301 	 * Removes the specified VM from the listings of VMs
302 	 * @param vm the VM to remove
303 	 */
remove(IVMInstall vm)304 	void remove(IVMInstall vm) {
305 		fCompatibleVMs.remove(vm);
306 		fStrictlyCompatible.remove(vm);
307 	}
308 
309 	/**
310 	 * Sets the default VM to be the one specified
311 	 * @param vm the VM to set as the default
312 	 */
initDefaultVM(IVMInstall vm)313 	void initDefaultVM(IVMInstall vm) {
314 		fDefault = vm;
315 	}
316 
317 	/* (non-Javadoc)
318 	 * @see org.eclipse.jdt.launching.environments.IExecutionEnvironment#getAccessRules(org.eclipse.jdt.launching.IVMInstall, org.eclipse.jdt.launching.LibraryLocation[], org.eclipse.jdt.core.IJavaProject)
319 	 */
320 	@Override
getAccessRules(IVMInstall vm, LibraryLocation[] libraries, IJavaProject project)321 	public IAccessRule[][] getAccessRules(IVMInstall vm, LibraryLocation[] libraries, IJavaProject project) {
322 		IAccessRuleParticipant[] participants = getParticipants();
323 		Map<IAccessRuleParticipant, IAccessRule[][]> rulesByParticipant = collectRulesByParticipant(participants, vm, libraries, project);
324 		synchronized (this) {
325 			Map<IAccessRuleParticipant, IAccessRule[][]> cachedRules = fParticipantMap.get(vm);
326 			if (cachedRules == null || !cachedRules.equals(rulesByParticipant)) {
327 				ArrayList<List<IAccessRule>> libLists = new ArrayList<>(); // array of lists of access rules
328 				for (int i = 0; i < libraries.length; i++) {
329 					libLists.add(new ArrayList<IAccessRule>());
330 				}
331 				for (int i = 0; i < participants.length; i++) {
332 					IAccessRuleParticipant participant = participants[i];
333 					addRules(rulesByParticipant.get(participant), libLists);
334 				}
335 				IAccessRule[][] allRules = new IAccessRule[libraries.length][];
336 				for (int i = 0; i < libLists.size(); i++) {
337 					List<IAccessRule> l = libLists.get(i);
338 					allRules[i] = l.toArray(new IAccessRule[l.size()]);
339 				}
340 				fParticipantMap.put(vm, rulesByParticipant);
341 				fRuleCache.put(vm, allRules);
342 				return allRules;
343 			}
344 			return fRuleCache.get(vm);
345 		}
346 	}
347 
348 	/**
349 	 * Returns all access rule participants to consider for this environment.
350 	 * Includes any participant contributed with this environment and all other
351 	 * stand alone participants.
352 	 *
353 	 * @return access rule participants to consider for this environment
354 	 */
getParticipants()355 	private synchronized IAccessRuleParticipant[] getParticipants() {
356 		if (fParticipants == null) {
357 			// check participants first
358 			IAccessRuleParticipant[] participants = EnvironmentsManager.getDefault().getAccessRuleParticipants();
359 			if (fRuleParticipant != null) {
360 				// ensure environment specific provider is last and not duplicated
361 				LinkedHashSet<IAccessRuleParticipant> set = new LinkedHashSet<>();
362 				for (int i = 0; i < participants.length; i++) {
363 					set.add(participants[i]);
364 				}
365 				// remove, add to make last
366 				set.remove(fRuleParticipant);
367 				set.add(fRuleParticipant);
368 				participants = set.toArray(new IAccessRuleParticipant[set.size()]);
369 			}
370 			fParticipants = participants;
371 		}
372 		return fParticipants;
373 	}
374 
375 	/**
376 	 * Returns a map of participant to the access rules for that participant for the given
377 	 * VM, libraries, and project.
378 	 *
379 	 * @param participants access rule participants
380 	 * @param vm the VM
381 	 * @param libraries the {@link LibraryLocation}s
382 	 * @param project the {@link IJavaProject} context
383 	 * @return the mapping of {@link IAccessRuleParticipant} to {@link IAccessRule}s
384 	 */
collectRulesByParticipant(IAccessRuleParticipant[] participants, IVMInstall vm, LibraryLocation[] libraries, IJavaProject project)385 	private Map<IAccessRuleParticipant, IAccessRule[][]> collectRulesByParticipant(IAccessRuleParticipant[] participants, IVMInstall vm, LibraryLocation[] libraries, IJavaProject project) {
386 		Map<IAccessRuleParticipant, IAccessRule[][]> map = new HashMap<>();
387 		for (int i = 0; i < participants.length; i++) {
388 			// TODO: use safe runnable
389 			map.put(participants[i], participants[i].getAccessRules(this, vm, libraries, project));
390 		}
391 		return map;
392 	}
393 
394 	/**
395 	 * Adds the access rules to each list in the given collection. If the last rule in a
396 	 * given collection is the wild card pattern then no more rules are added to that collection.
397 	 *
398 	 * @param accessRules the list of {@link IAccessRule}s
399 	 * @param collect the array of lists to collect the {@link IAccessRule}s in
400 	 */
addRules(IAccessRule[][] accessRules, ArrayList<List<IAccessRule>> collect)401 	private void addRules(IAccessRule[][] accessRules, ArrayList<List<IAccessRule>> collect) {
402 		for (int i = 0; i < accessRules.length; i++) {
403 			IAccessRule[] libRules = accessRules[i];
404 			List<IAccessRule> list = collect.get(i);
405 			// if the last rule is a **/* pattern, don't add any more rules, as they will have no effect
406 			if (!list.isEmpty()) {
407 				IAccessRule lastRule = list.get(list.size() - 1);
408 				if(lastRule.getPattern().equals(ALL_PATTERN)) {
409 					continue;
410 				}
411 			}
412 			for (int j = 0; j < libRules.length; j++) {
413 				list.add(libRules[j]);
414 			}
415 		}
416 	}
417 
418 	/* (non-Javadoc)
419 	 * @see org.eclipse.jdt.launching.environments.IExecutionEnvironment#getProfileProperties()
420 	 */
421 	@Override
getProfileProperties()422 	public Properties getProfileProperties() {
423 		if (!fPropertiesInitialized) {
424 			fPropertiesInitialized = true;
425 			String path = fElement.getAttribute("profileProperties"); //$NON-NLS-1$
426 			Bundle bundle = null;
427 			if (path == null) {
428 				// attempt default profiles known to OSGi
429 				bundle = Platform.getBundle("org.eclipse.osgi"); //$NON-NLS-1$
430 				path = getId().replace('/', '_') + ".profile"; //$NON-NLS-1$
431 			} else {
432 				// read provided file
433 				bundle = Platform.getBundle(fElement.getContributor().getName());
434 			}
435 			if (bundle != null && path != null) {
436 				fProfileProperties = getJavaProfileProperties(bundle, path);
437 			}
438 		}
439 		return fProfileProperties;
440 	}
441 
442 	/**
443 	 * Returns properties file contained in the specified bundle at the given
444 	 * bundle relative path, or <code>null</code> if none.
445 	 *
446 	 * @param bundle bundle to locate file in
447 	 * @param path bundle relative path to properties file
448 	 * @return properties or <code>null</code> if none
449 	 */
getJavaProfileProperties(Bundle bundle, String path)450 	private Properties getJavaProfileProperties(Bundle bundle, String path) {
451 		Properties profile = new Properties();
452 		URL profileURL = bundle.getEntry(path);
453 		if (profileURL != null) {
454 			try (InputStream is = profileURL.openStream()) {
455 				profileURL = FileLocator.resolve(profileURL);
456 				if (is != null) {
457 					profile.load(is);
458 					fixJavaSE9ComplianceSourceTargetLevels(profile);
459 				}
460 			} catch (IOException e) {
461 				return null;
462 			}
463 		} else {
464 			String compliance = getCompliance();
465 			if (compliance == null) {
466 				return null;
467 			}
468 			profile.setProperty(JavaCore.COMPILER_COMPLIANCE, compliance);
469 			profile.setProperty(JavaCore.COMPILER_SOURCE, compliance);
470 			profile.setProperty(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, compliance);
471 			profile.setProperty(JavaCore.COMPILER_PB_ASSERT_IDENTIFIER, JavaCore.ERROR);
472 			profile.setProperty(JavaCore.COMPILER_PB_ENUM_IDENTIFIER, JavaCore.ERROR);
473 			profile.setProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, calculateVMExecutionEnvs(new Version(compliance)));
474 			profile.setProperty(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED);
475 			if (JavaCore.compareJavaVersions(compliance, JavaCore.VERSION_10) > 0) {
476 				profile.setProperty(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.DISABLED);
477 				profile.setProperty(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.WARNING);
478 			}
479 
480 		}
481 		// For below Java 9 EE, compiler release option should be disabled by default
482 		String property = profile.getProperty(JavaCore.COMPILER_COMPLIANCE);
483 		if (property != null) {
484 			if (JavaCore.compareJavaVersions(property, JavaCore.VERSION_9) < 0) {
485 				profile.setProperty(JavaCore.COMPILER_RELEASE, JavaCore.DISABLED);
486 			}
487 		}
488 		return profile;
489 	}
490 
491 	private static final String JAVASE = "JavaSE"; //$NON-NLS-1$
492 
calculateVMExecutionEnvs(Version javaVersion)493 	private String calculateVMExecutionEnvs(Version javaVersion) {
494 		StringBuilder result = new StringBuilder("OSGi/Minimum-1.0, OSGi/Minimum-1.1, OSGi/Minimum-1.2, JavaSE/compact1-1.8, JavaSE/compact2-1.8, JavaSE/compact3-1.8, JRE-1.1, J2SE-1.2, J2SE-1.3, J2SE-1.4, J2SE-1.5, JavaSE-1.6, JavaSE-1.7, JavaSE-1.8"); //$NON-NLS-1$
495 		Version v = new Version(9, 0, 0);
496 		while (v.compareTo(javaVersion) <= 0) {
497 			result.append(',').append(' ').append(JAVASE).append('-').append(v.getMajor());
498 			if (v.getMinor() > 0) {
499 				result.append('.').append(v.getMinor());
500 			}
501 			if (v.getMajor() == javaVersion.getMajor()) {
502 				v = new Version(v.getMajor(), v.getMinor() + 1, 0);
503 			} else {
504 				v = new Version(v.getMajor() + 1, 0, 0);
505 			}
506 		}
507 		return result.toString();
508 	}
509 
510 
511 	/**
512 	 * Bug 470616: [1.9] JavaSE-9 Execution Environment should set proper compiler compliance/source/target levels
513 	 * <p>
514 	 * This is a workaround for Bug 495497: [9] JavaSE-9.profile Execution Environment should set compiler levels to 9
515 	 */
fixJavaSE9ComplianceSourceTargetLevels(Properties profile)516 	private void fixJavaSE9ComplianceSourceTargetLevels(Properties profile) {
517 		if (ExecutionEnvironmentAnalyzer.JavaSE_9.equals(getId())) {
518 			profile.setProperty(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_9);
519 			profile.setProperty(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_9);
520 			profile.setProperty(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_9);
521 			profile.setProperty(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED);
522 		}
523 	}
524 
525 	/* (non-Javadoc)
526 	 * @see org.eclipse.jdt.launching.environments.IExecutionEnvironment#getSubEnvironments()
527 	 */
528 	@Override
getSubEnvironments()529 	public IExecutionEnvironment[] getSubEnvironments() {
530 		Properties properties = getProfileProperties();
531 		Set<IExecutionEnvironment> subenv = new LinkedHashSet<>();
532 		if (properties != null) {
533 			@SuppressWarnings("deprecation")
534 			String subsets = properties.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT);
535 			if (subsets != null) {
536 				String[] ids = subsets.split(","); //$NON-NLS-1$
537 				for (int i = 0; i < ids.length; i++) {
538 					IExecutionEnvironment sub = JavaRuntime.getExecutionEnvironmentsManager().getEnvironment(ids[i].trim());
539 					if (sub != null && !sub.getId().equals(getId())) {
540 						subenv.add(sub);
541 					}
542 				}
543 			}
544 		}
545 		return subenv.toArray(new IExecutionEnvironment[subenv.size()]);
546 	}
547 
548 	/* (non-Javadoc)
549 	 * @see org.eclipse.jdt.launching.environments.IExecutionEnvironment#getComplianceOptions()
550 	 */
551 	@Override
getComplianceOptions()552 	public Map<String, String> getComplianceOptions() {
553 		Properties properties = getProfileProperties();
554 		if (properties != null) {
555 			Map<String, String> map = new HashMap<>();
556 			Iterator<?> iterator = properties.keySet().iterator();
557 			while (iterator.hasNext()) {
558 				String key = (String) iterator.next();
559 				if (key.startsWith(COMPILER_SETTING_PREFIX)) {
560 					map.put(key, properties.getProperty(key));
561 				}
562 			}
563 			if (!map.isEmpty()) {
564 				return map;
565 			}
566 		}
567 		return null;
568 	}
569 
getCompliance()570 	private String getCompliance() {
571 		return fElement.getAttribute("compliance"); //$NON-NLS-1$
572 	}
573 
574 	@Override
toString()575 	public String toString() {
576 		return this.fElement.getAttribute("id"); //$NON-NLS-1$
577 	}
578 }
579