| /* |
| * Copyright (c) 2010-2020 BSI Business Systems Integration AG. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * BSI Business Systems Integration AG - initial API and implementation |
| */ |
| package org.eclipse.scout.sdk.core.model.ecj; |
| |
| import static java.util.Collections.emptyMap; |
| import static java.util.Collections.unmodifiableMap; |
| import static java.util.stream.Collectors.collectingAndThen; |
| import static java.util.stream.Collectors.toList; |
| import static org.eclipse.scout.sdk.core.log.SdkLog.onTrace; |
| import static org.eclipse.scout.sdk.core.util.Ensure.fail; |
| import static org.eclipse.scout.sdk.core.util.Ensure.newFail; |
| |
| import java.nio.CharBuffer; |
| import java.nio.file.FileSystemAlreadyExistsException; |
| import java.nio.file.Path; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.function.Function; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.Annotation; |
| import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.Argument; |
| import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.ImportReference; |
| import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeParameter; |
| import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ElementValuePair; |
| import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; |
| import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; |
| import org.eclipse.scout.sdk.core.log.SdkLog; |
| import org.eclipse.scout.sdk.core.model.api.ISourceRange; |
| import org.eclipse.scout.sdk.core.model.api.internal.SourceRange; |
| import org.eclipse.scout.sdk.core.model.ecj.SourcePositionComparators.MethodBindingComparator; |
| import org.eclipse.scout.sdk.core.model.spi.AbstractJavaEnvironment; |
| import org.eclipse.scout.sdk.core.model.spi.AnnotatableSpi; |
| import org.eclipse.scout.sdk.core.model.spi.AnnotationElementSpi; |
| import org.eclipse.scout.sdk.core.model.spi.AnnotationSpi; |
| import org.eclipse.scout.sdk.core.model.spi.ClasspathSpi; |
| import org.eclipse.scout.sdk.core.model.spi.CompilationUnitSpi; |
| import org.eclipse.scout.sdk.core.model.spi.JavaElementSpi; |
| import org.eclipse.scout.sdk.core.model.spi.JavaEnvironmentSpi; |
| import org.eclipse.scout.sdk.core.model.spi.PackageSpi; |
| import org.eclipse.scout.sdk.core.model.spi.TypeSpi; |
| import org.eclipse.scout.sdk.core.util.CompositeObject; |
| import org.eclipse.scout.sdk.core.util.Ensure; |
| import org.eclipse.scout.sdk.core.util.FinalValue; |
| import org.eclipse.scout.sdk.core.util.JavaTypes; |
| import org.eclipse.scout.sdk.core.util.Strings; |
| |
| /** |
| * <h3>{@link JavaEnvironmentWithEcj}</h3> |
| * |
| * @since 5.1.0 |
| */ |
| public class JavaEnvironmentWithEcj extends AbstractJavaEnvironment implements AutoCloseable { |
| |
| // arguments (survives reinitialization) |
| private final Path m_javaHome; |
| private final CompilerOptions m_options; // may be null |
| private final Collection<? extends ClasspathEntry> m_rawClassPath; |
| |
| // caches |
| private final Map<Object, JavaElementSpi> m_elements; // all except TypeSpi. Types are stored in super class |
| private final Map<ReferenceBinding, Map<String, ElementValuePair>> m_evpCache; // cache for annotation elements |
| private final Map<TypeBinding, Map<String, MemberValuePair>> m_mvpCache; // cache for annotation elements |
| private final Map<CharBuffer /* file path */, char[] /* source contents */> m_sourceCache; // cache for file source |
| private FinalValue<FileSystemWithOverride> m_fs; |
| private FinalValue<EcjAstCompiler> m_compiler; |
| private FinalValue<List<ClasspathSpi>> m_classpath; |
| |
| // state |
| private volatile boolean m_initialized; |
| |
| protected JavaEnvironmentWithEcj(Path javaHome, Collection<? extends ClasspathEntry> classpath, CompilerOptions options) { |
| m_javaHome = javaHome; |
| m_options = options; |
| m_rawClassPath = new ArrayList<>(classpath); |
| |
| m_elements = new ConcurrentHashMap<>(); |
| m_evpCache = new ConcurrentHashMap<>(); |
| m_mvpCache = new ConcurrentHashMap<>(); |
| m_sourceCache = new HashMap<>(); // normal map is ok: all access is synchronized |
| m_fs = new FinalValue<>(); |
| m_compiler = new FinalValue<>(); |
| m_classpath = new FinalValue<>(); |
| m_initialized = true; |
| } |
| |
| @Override |
| protected TypeSpi doFindType(String fqn) { |
| assertInitialized(); |
| TypeNameDescriptor desc = TypeNameDescriptor.of(fqn); |
| TypeBinding binding = lookupTypeBinding(desc.getPrimaryTypeName()); |
| if (binding == null) { |
| return null; |
| } |
| |
| TypeSpi result; |
| if (desc.hasInnerType()) { |
| result = SpiWithEcjUtils.bindingToInnerType(this, binding, desc.getInnerTypeNames()); |
| } |
| else { |
| // no inner types: directly use answer |
| result = SpiWithEcjUtils.bindingToType(this, binding); |
| } |
| |
| if (desc.getArrayDimension() > 0) { |
| TypeBinding b = Ensure.notNull(((AbstractTypeWithEcj) result).getInternalBinding(), "Cannot find internal binding to create array type."); |
| ArrayBinding arrayType; |
| arrayType = getCompiler().lookupEnvironment.createArrayType(b, desc.getArrayDimension()); |
| return SpiWithEcjUtils.bindingToType(this, arrayType); |
| } |
| return result; |
| } |
| |
| protected TypeBinding lookupTypeBinding(String fqn) { |
| if (fqn.length() <= 7) { |
| switch (fqn) { |
| case JavaTypes._boolean: |
| return TypeBinding.BOOLEAN; |
| case JavaTypes._char: |
| return TypeBinding.CHAR; |
| case JavaTypes._byte: |
| return TypeBinding.BYTE; |
| case JavaTypes._short: |
| return TypeBinding.SHORT; |
| case JavaTypes._int: |
| return TypeBinding.INT; |
| case JavaTypes._long: |
| return TypeBinding.LONG; |
| case JavaTypes._float: |
| return TypeBinding.FLOAT; |
| case JavaTypes._double: |
| return TypeBinding.DOUBLE; |
| case JavaTypes._void: |
| return TypeBinding.VOID; |
| default: |
| // all short class names continue |
| break; |
| } |
| } |
| char[][] lookupName = CharOperation.splitOn(JavaTypes.C_DOT, fqn.toCharArray()); |
| ReferenceBinding binding; |
| synchronized (lock()) { |
| binding = getCompiler().lookupEnvironment.getType(lookupName); |
| } |
| if (binding instanceof MissingTypeBinding) { |
| return null; |
| } |
| return binding; |
| } |
| |
| @Override |
| protected Collection<JavaElementSpi> allElements() { |
| return m_elements.values(); |
| } |
| |
| /** |
| * @return A {@link Path} to the JRE (not JDK!) home. Never returns {@code null}. |
| */ |
| public Path javaHome() { |
| return m_javaHome; |
| } |
| |
| @Override |
| protected void reinitialize() { |
| runPreservingOverrides(this, this, this::doReinitialize); |
| } |
| |
| private void doReinitialize() { |
| clear(); |
| m_initialized = true; |
| } |
| |
| protected boolean isInitialized() { |
| return m_initialized; |
| } |
| |
| protected void assertInitialized() { |
| if (isInitialized()) { |
| return; |
| } |
| fail("JavaEnvironment has already been closed."); |
| } |
| |
| @Override |
| public void close() { |
| runPreservingOverrides(this, this, this::doClose); |
| } |
| |
| private void doClose() { |
| clear(); |
| m_initialized = false; |
| } |
| |
| private void clear() { |
| super.reinitialize(); |
| m_elements.clear(); |
| m_evpCache.clear(); |
| m_mvpCache.clear(); |
| m_sourceCache.clear(); |
| |
| FileSystemWithOverride filesystem = m_fs.get(); |
| m_fs = new FinalValue<>(); // remove the current filesystem before closing it |
| if (filesystem != null) { |
| filesystem.cleanup(); |
| } |
| m_compiler = new FinalValue<>(); |
| m_classpath = new FinalValue<>(); |
| } |
| |
| @Override |
| public <T> T callInEmptyCopy(Function<JavaEnvironmentSpi, T> function) { |
| try (JavaEnvironmentWithEcj copy = emptyCopy()) { |
| return Ensure.notNull(function).apply(copy); |
| } |
| } |
| |
| protected JavaEnvironmentWithEcj emptyCopy() { |
| JavaEnvironmentWithEcj newEnv = new JavaEnvironmentWithEcj(javaHome(), getNameEnvironment().classpath(), getCompiler().options); |
| runPreservingOverrides(this, newEnv, null); // copy overrides |
| return newEnv; |
| } |
| |
| /** |
| * Executes the specified {@link Runnable} ensuring that the overridden compilation units of the source environment |
| * are also available in the destination environment after the runnable has been executed. |
| * |
| * @param src |
| * The source environment |
| * @param dest |
| * The destination environment |
| * @param task |
| * The task. May be {@code null} if nothing should be executed but only the overrides should be copied from |
| * src to dest. |
| */ |
| protected void runPreservingOverrides(JavaEnvironmentWithEcj src, JavaEnvironmentWithEcj dest, Runnable task) { |
| synchronized (lock()) { |
| Iterable<ICompilationUnit> units = new ArrayList<>(src.getNameEnvironment().overrideSupport().getCompilationUnits()); |
| if (task != null) { |
| task.run(); |
| } |
| for (ICompilationUnit cu : units) { |
| dest.getNameEnvironment().overrideSupport().addCompilationUnit(cu); |
| } |
| } |
| } |
| |
| @Override |
| public List<String> getCompileErrors(TypeSpi typeSpi) { |
| CompilationUnitSpi cuSpi = Ensure.notNull(typeSpi).getCompilationUnit(); |
| if (!(cuSpi instanceof DeclarationCompilationUnitWithEcj)) { |
| throw newFail("Type '{}' is not a source type.", typeSpi.getName()); |
| } |
| CompilationUnitDeclaration decl = ((DeclarationCompilationUnitWithEcj) cuSpi).getInternalCompilationUnitDeclaration(); |
| synchronized (lock()) { |
| return getCompiler().getCompileErrors(decl); |
| } |
| } |
| |
| @Override |
| public List<String> getCompileErrors(String fqn) { |
| return getCompileErrors(Ensure.notNull(findType(fqn), "Cannot find type '{}'.", fqn)); |
| } |
| |
| @Override |
| public boolean registerCompilationUnitOverride(String packageName, String fileName, char[] src) { |
| Ensure.notNull(fileName); |
| Ensure.notNull(src); |
| |
| StringBasedCompilationUnitWithEcj cu = new StringBasedCompilationUnitWithEcj(packageName, fileName, src, ""/*ModuleBinding.UNNAMED*/, fileName); |
| synchronized (lock()) { |
| boolean reloadRequired = getNameEnvironment().overrideSupport().addCompilationUnit(cu); |
| |
| String fqn = cu.getFullyQualifiedName(); |
| removeTypeFromCache(fqn);// clear cache info for this element |
| if (!reloadRequired && isInitialized()) { |
| // if not used in name-env: also check in compiler |
| reloadRequired = isLoadedInCompiler(fqn, src); |
| } |
| |
| // ensure the package of the new override CU exists. It may be in the lookupEnv cache as 'notExisting' from a call before where it really did not exist. |
| if (!Strings.isEmpty(packageName) && isInitialized()) { |
| getCompiler().lookupEnvironment.createPackage(cu.getPackageName()); |
| } |
| |
| if (reloadRequired) { |
| m_sourceCache.keySet().removeIf(cuFileName -> CharOperation.endsWith(cuFileName.array(), cu.getFileName())); |
| } |
| |
| return reloadRequired; |
| } |
| } |
| |
| private boolean isLoadedInCompiler(String fqn, char[] src) { |
| ReferenceBinding cachedType = findExistingBindingFor(fqn); |
| if (cachedType == null) { |
| return false; // has not yet been used. no reload required. |
| } |
| |
| if (cachedType instanceof SourceTypeBinding) { |
| CompilationUnitDeclaration decl = ((SourceTypeBinding) cachedType).scope.compilationUnitScope().referenceContext; |
| char[] existing = getSource(decl); |
| return !Arrays.equals(existing, src); // only reload if source is different |
| } |
| return true; |
| } |
| |
| private ReferenceBinding findExistingBindingFor(String fqn) { |
| LookupEnvironment lookupEnvironment = getCompiler().lookupEnvironment; |
| char[][] compoundName = CharOperation.splitOn(JavaTypes.C_DOT, fqn.toCharArray()); |
| ReferenceBinding cachedType = lookupEnvironment.getCachedType(compoundName); |
| if (cachedType != null) { |
| return cachedType; |
| } |
| |
| for (ModuleBinding module : lookupEnvironment.knownModules.valueTable) { |
| if (module == null) { |
| continue; |
| } |
| ReferenceBinding referenceBinding = module.environment.getCachedType(compoundName); |
| if (referenceBinding != null) { |
| return referenceBinding; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @param cu |
| * the compilation unit to get the source for |
| * @return the source of the compilation unit or null. The source is only available if the compilation unit is one of |
| * the following |
| * <ul> |
| * <li>source in workspace</li> |
| * <li>class in jar and source in same jar</li> |
| * <li>class in jar and source attachment to jar is defined</li> |
| * </ul> |
| */ |
| public ISourceRange getSource(CompilationUnitSpi cu, int start, int end) { |
| if (!(cu instanceof DeclarationCompilationUnitWithEcj)) { |
| return null; |
| } |
| assertInitialized(); |
| |
| char[] src = getSource(((DeclarationCompilationUnitWithEcj) cu).getInternalCompilationUnitDeclaration()); |
| if (src == null) { |
| return null; |
| } |
| |
| return new SourceRange(CharBuffer.wrap(src, start, end - start + 1), start, end); |
| } |
| |
| @SuppressWarnings("squid:S1168") |
| protected char[] getSource(CompilationUnitDeclaration decl) { |
| synchronized (lock()) { |
| ICompilationUnit sourceUnit = getCompiler().getSource(decl); |
| if (sourceUnit == null) { |
| return null; |
| } |
| |
| return m_sourceCache.computeIfAbsent(CharBuffer.wrap(sourceUnit.getFileName()), k -> sourceUnit.getContents()); |
| } |
| } |
| |
| protected FileSystemWithOverride getNameEnvironment() { |
| return m_fs.computeIfAbsentAndGet(this::buildNameEnvironment); |
| } |
| |
| private FileSystemWithOverride buildNameEnvironment() { |
| // classpath registers a system wide file system but does not handle the fact that it might already have been created. |
| // see org.eclipse.jdt.internal.compiler.batch.ClasspathMultiReleaseJar.initialize |
| ClasspathBuilder cp = new ClasspathBuilder(javaHome(), m_rawClassPath); |
| while (true) { |
| try { |
| // optimistic creation without locking |
| return new FileSystemWithOverride(cp); |
| } |
| catch (FileSystemAlreadyExistsException e) { |
| SdkLog.debug("Concurrent registration of process wide filesystem.", onTrace(e)); |
| } |
| } |
| } |
| |
| /** |
| * Only use the compiler under lock of {@link #lock()}! |
| */ |
| private EcjAstCompiler getCompiler() { |
| return m_compiler.computeIfAbsentAndGet(() -> { |
| CompilerOptions opts = m_options == null ? EcjAstCompiler.createDefaultOptions() : m_options; |
| return new EcjAstCompiler(getNameEnvironment(), opts, lock()); |
| }); |
| } |
| |
| @Override |
| public List<ClasspathSpi> getClasspath() { |
| return m_classpath.computeIfAbsentAndGet(() -> getNameEnvironment().classpath().stream() |
| .map(this::classpathEntryToSpi) |
| .collect(collectingAndThen(toList(), Collections::unmodifiableList))); |
| } |
| |
| public VoidTypeWithEcj createVoidType() { |
| assertInitialized(); |
| synchronized (lock()) { |
| return (VoidTypeWithEcj) m_elements.computeIfAbsent(VoidTypeWithEcj.class, k -> new VoidTypeWithEcj(this)); |
| } |
| } |
| |
| public WildcardOnlyTypeWithEcj createWildcardOnlyType() { |
| assertInitialized(); |
| synchronized (lock()) { |
| return (WildcardOnlyTypeWithEcj) m_elements.computeIfAbsent(WildcardOnlyTypeWithEcj.class, k -> new WildcardOnlyTypeWithEcj(this)); |
| } |
| } |
| |
| public BindingAnnotationWithEcj createBindingAnnotation(AnnotatableSpi owner, AnnotationBinding binding) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(binding); |
| return (BindingAnnotationWithEcj) m_elements.computeIfAbsent(key, k -> new BindingAnnotationWithEcj(this, owner, binding)); |
| } |
| } |
| |
| public BindingAnnotationElementWithEcj createBindingAnnotationValue(AnnotationSpi owner, ElementValuePair bindingPair, boolean syntheticDefaultValue) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(owner, bindingPair); |
| return (BindingAnnotationElementWithEcj) m_elements.computeIfAbsent(key, k -> new BindingAnnotationElementWithEcj(this, owner, bindingPair, syntheticDefaultValue)); |
| } |
| } |
| |
| public NullAnnotationElementWithEcj createNullAnnotationValue(AnnotationSpi owner, String name, boolean syntheticDefaultValue) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(NullAnnotationElementWithEcj.class, owner, name); |
| return (NullAnnotationElementWithEcj) m_elements.computeIfAbsent(key, k -> new NullAnnotationElementWithEcj(this, owner, name, syntheticDefaultValue)); |
| } |
| } |
| |
| public BindingArrayTypeWithEcj createBindingArrayType(ArrayBinding binding, boolean isWildcard) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(binding, isWildcard); |
| return (BindingArrayTypeWithEcj) m_elements.computeIfAbsent(key, k -> new BindingArrayTypeWithEcj(this, binding, isWildcard)); |
| } |
| } |
| |
| public BindingBaseTypeWithEcj createBindingBaseType(BaseTypeBinding binding) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(binding); |
| return (BindingBaseTypeWithEcj) m_elements.computeIfAbsent(key, k -> new BindingBaseTypeWithEcj(this, binding)); |
| } |
| } |
| |
| public BindingFieldWithEcj createBindingField(AbstractTypeWithEcj declaringType, FieldBinding binding) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(binding); |
| return (BindingFieldWithEcj) m_elements.computeIfAbsent(key, k -> new BindingFieldWithEcj(this, declaringType, binding)); |
| } |
| } |
| |
| public BindingMethodWithEcj createBindingMethod(BindingTypeWithEcj declaringType, MethodBinding binding) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(binding); |
| return (BindingMethodWithEcj) m_elements.computeIfAbsent(key, k -> new BindingMethodWithEcj(this, declaringType, binding)); |
| } |
| } |
| |
| public BindingMethodParameterWithEcj createBindingMethodParameter(BindingMethodWithEcj declaringMethod, TypeBinding binding, char[] name, int flags, int index) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(BindingMethodParameterWithEcj.class, declaringMethod, binding, index); |
| return (BindingMethodParameterWithEcj) m_elements.computeIfAbsent(key, k -> new BindingMethodParameterWithEcj(this, declaringMethod, binding, name, flags, index)); |
| } |
| } |
| |
| public BindingTypeWithEcj createBindingType(ReferenceBinding binding, BindingTypeWithEcj declaringType, boolean isWildcard) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(binding, isWildcard); |
| return (BindingTypeWithEcj) m_elements.computeIfAbsent(key, k -> new BindingTypeWithEcj(this, binding, declaringType, isWildcard)); |
| } |
| } |
| |
| public BindingTypeParameterWithEcj createBindingTypeParameter(AbstractMemberWithEcj<?> declaringMember, TypeVariableBinding binding, int index) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(declaringMember, binding, index); |
| return (BindingTypeParameterWithEcj) m_elements.computeIfAbsent(key, k -> new BindingTypeParameterWithEcj(this, declaringMember, binding, index)); |
| } |
| } |
| |
| public DeclarationAnnotationWithEcj createDeclarationAnnotation(AnnotatableSpi owner, Annotation astNode) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(astNode); |
| return (DeclarationAnnotationWithEcj) m_elements.computeIfAbsent(key, k -> new DeclarationAnnotationWithEcj(this, owner, astNode)); |
| } |
| } |
| |
| public DeclarationAnnotationElementWithEcj createDeclarationAnnotationValue(AnnotationSpi declaringAnnotation, MemberValuePair astNode, boolean syntheticDefaultValue) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(astNode); |
| return (DeclarationAnnotationElementWithEcj) m_elements.computeIfAbsent(key, k -> new DeclarationAnnotationElementWithEcj(this, declaringAnnotation, astNode, syntheticDefaultValue)); |
| } |
| } |
| |
| public DeclarationCompilationUnitWithEcj createDeclarationCompilationUnit(CompilationUnitDeclaration astNode) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(astNode); |
| return (DeclarationCompilationUnitWithEcj) m_elements.computeIfAbsent(key, k -> new DeclarationCompilationUnitWithEcj(this, astNode)); |
| } |
| } |
| |
| public DeclarationFieldWithEcj createDeclarationField(DeclarationTypeWithEcj declaringType, FieldDeclaration astNode) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(astNode); |
| return (DeclarationFieldWithEcj) m_elements.computeIfAbsent(key, k -> new DeclarationFieldWithEcj(this, declaringType, astNode)); |
| } |
| } |
| |
| public DeclarationImportWithEcj createDeclarationImport(DeclarationCompilationUnitWithEcj owner, ImportReference astNode) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(astNode); |
| return (DeclarationImportWithEcj) m_elements.computeIfAbsent(key, k -> new DeclarationImportWithEcj(this, owner, astNode)); |
| } |
| } |
| |
| public DeclarationMethodWithEcj createDeclarationMethod(DeclarationTypeWithEcj declaringType, AbstractMethodDeclaration astNode) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(astNode); |
| return (DeclarationMethodWithEcj) m_elements.computeIfAbsent(key, k -> new DeclarationMethodWithEcj(this, declaringType, astNode)); |
| } |
| } |
| |
| public DeclarationMethodParameterWithEcj createDeclarationMethodParameter(DeclarationMethodWithEcj declaringMethod, Argument astNode, int index) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(astNode); |
| return (DeclarationMethodParameterWithEcj) m_elements.computeIfAbsent(key, k -> new DeclarationMethodParameterWithEcj(this, declaringMethod, astNode, index)); |
| } |
| } |
| |
| public DeclarationTypeWithEcj createDeclarationType(CompilationUnitSpi cu, DeclarationTypeWithEcj declaringType, TypeDeclaration astNode) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(astNode); |
| return (DeclarationTypeWithEcj) m_elements.computeIfAbsent(key, k -> new DeclarationTypeWithEcj(this, cu, declaringType, astNode)); |
| } |
| } |
| |
| public DeclarationTypeParameterWithEcj createDeclarationTypeParameter(AbstractMemberWithEcj<?> declaringMember, TypeParameter astNode, int index) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(astNode); |
| return (DeclarationTypeParameterWithEcj) m_elements.computeIfAbsent(key, k -> new DeclarationTypeParameterWithEcj(this, declaringMember, astNode, index)); |
| } |
| } |
| |
| public PackageWithEcj createPackage(String name) { |
| assertInitialized(); |
| synchronized (lock()) { |
| CompositeObject key = new CompositeObject(PackageWithEcj.class, name); |
| return (PackageWithEcj) m_elements.computeIfAbsent(key, k -> new PackageWithEcj(this, name)); |
| } |
| } |
| |
| public PackageWithEcj createDefaultPackage() { |
| return createPackage(null); |
| } |
| |
| @Override |
| public PackageSpi getPackage(String name) { |
| return createPackage(name); |
| } |
| |
| public SyntheticCompilationUnitWithEcj createSyntheticCompilationUnit(BindingTypeWithEcj mainType) { |
| assertInitialized(); |
| synchronized (lock()) { |
| SameCompositeObject key = new SameCompositeObject(SyntheticCompilationUnitWithEcj.class, mainType); |
| return (SyntheticCompilationUnitWithEcj) m_elements.computeIfAbsent(key, k -> new SyntheticCompilationUnitWithEcj(this, mainType)); |
| } |
| } |
| |
| /** |
| * @return the (cached) default values {@link AnnotationElementSpi#isDefaultValue()} for the annotation in correct |
| * source order of the annotation type declaration |
| */ |
| public Map<String, ElementValuePair> getBindingAnnotationSyntheticDefaultValues(ReferenceBinding annotationType) { |
| assertInitialized(); |
| synchronized (lock()) { |
| return m_evpCache.computeIfAbsent(annotationType, JavaEnvironmentWithEcj::computeBindingAnnotationSyntheticDefaultValues); |
| } |
| } |
| |
| protected static Map<String, ElementValuePair> computeBindingAnnotationSyntheticDefaultValues(ReferenceBinding annotationType) { |
| MethodBinding[] valueMethods = annotationType.methods(); |
| if (valueMethods == null || valueMethods.length < 1) { |
| return emptyMap(); |
| } |
| |
| valueMethods = Arrays.copyOf(valueMethods, valueMethods.length); |
| Arrays.sort(valueMethods, MethodBindingComparator.INSTANCE); |
| |
| Map<String, ElementValuePair> defaultValues = new LinkedHashMap<>(valueMethods.length); |
| for (MethodBinding mb : valueMethods) { |
| String name = new String(mb.selector); |
| Object value = mb.getDefaultValue(); |
| if (value != null) { |
| defaultValues.put(name, new ElementValuePair(mb.selector, value, mb)); |
| } |
| else { |
| defaultValues.put(name, null); |
| } |
| } |
| return unmodifiableMap(defaultValues); |
| } |
| |
| public Map<String, MemberValuePair> getDeclarationAnnotationSyntheticDefaultValues(TypeBinding typeBinding) { |
| assertInitialized(); |
| synchronized (lock()) { |
| return m_mvpCache.computeIfAbsent(typeBinding, JavaEnvironmentWithEcj::computeDeclarationAnnotationSyntheticDefaultValues); |
| } |
| } |
| |
| protected static Map<String, MemberValuePair> computeDeclarationAnnotationSyntheticDefaultValues(TypeBinding typeBinding) { |
| MethodBinding[] valueMethods = ((ReferenceBinding) typeBinding).methods(); |
| if (valueMethods == null || valueMethods.length < 1) { |
| return emptyMap(); |
| } |
| |
| valueMethods = Arrays.copyOf(valueMethods, valueMethods.length); |
| Arrays.sort(valueMethods, MethodBindingComparator.INSTANCE); |
| |
| Map<String, MemberValuePair> defaultValues = new LinkedHashMap<>(valueMethods.length); |
| for (MethodBinding mb : valueMethods) { |
| String name = new String(mb.selector); |
| AbstractMethodDeclaration md0 = Ensure.notNull(SpiWithEcjUtils.sourceMethodOf(mb), "binding is binary. Source method could not be found."); |
| if (md0 instanceof AnnotationMethodDeclaration) { |
| AnnotationMethodDeclaration md = (AnnotationMethodDeclaration) md0; |
| if (md.defaultValue != null) { |
| defaultValues.put(name, new MemberValuePair(mb.selector, md.defaultValue.sourceStart, md.defaultValue.sourceEnd, md.defaultValue)); |
| } |
| else { |
| defaultValues.put(name, null); |
| } |
| } |
| } |
| return unmodifiableMap(defaultValues); |
| } |
| |
| protected ClasspathSpi classpathEntryToSpi(ClasspathEntry entry) { |
| return new ClasspathWithEcj(entry, this); |
| } |
| } |