Java tutorial
/** * Copyright (c) 2016 NumberFour 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: * NumberFour AG - Initial API and implementation */ package eu.numberfour.n4js.resource; import static com.google.common.collect.Maps.newHashMap; import java.util.Collections; import java.util.Map; import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.naming.IQualifiedNameProvider; import org.eclipse.xtext.naming.QualifiedName; import org.eclipse.xtext.resource.EObjectDescription; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy; import org.eclipse.xtext.util.IAcceptor; import com.google.common.collect.ForwardingMap; import com.google.inject.Inject; import com.google.inject.Singleton; import eu.numberfour.n4js.AnnotationDefinition; import eu.numberfour.n4js.ts.types.TClass; import eu.numberfour.n4js.ts.types.TMethod; import eu.numberfour.n4js.ts.types.TModule; import eu.numberfour.n4js.ts.types.TVariable; import eu.numberfour.n4js.ts.types.Type; /** * Only produce {@link EObjectDescription descriptions} for instances of the type-representation of the resource. */ @Singleton public class N4JSResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy { /** * User data to store the {@link TModule#isMainModule() mainModule} property of a {@link TModule} in the index * without resolving the actual proxy. This property distinguishes between projects main module and other modules. * Main module has different shadowing rules in the scoping. Note: Only {@link IEObjectDescription object * descriptions} representing a {@link TModule} have this user data. */ public static final String MAIN_MODULE_KEY = "MAIN_MODULE_KEY"; /** * The user data key for the type access modifier - used to compute visibility without creating a resource just from * the proxy in the index. */ public static final String ACCESS_MODIFIERY_KEY = "ACCESS_MODIFIER_KEY"; /** * User data to store the {@link TClass#isFinal() final} property of a {@link TClass} in the index without resolving * the actual proxy. Used by N4JS type search. Note: Only {@link IEObjectDescription object description}s * representing a {@link TClass} has this user data. */ public static final String FINAL_KEY = "FINAL_KEY"; /** * User data to store the {@link TClass#isAbstract() abstract} property of a {@link TClass} in the index without * resolving the actual proxy. Used by N4JS type search. Note: Only {@link IEObjectDescription object description}s * representing a {@link TClass} has this user data. */ public static final String ABSTRACT_KEY = "ABSTRACT_KEY"; /** * User data telling if a {@link TClass} in the index is a test class, i.e. contains one or more methods annotated * with @Test, without resolving the actual proxy. Used by test discovery. Note: Only {@link IEObjectDescription * object description}s representing a {@link TClass} have this user data. */ public static final String TEST_CLASS_KEY = "TEST_CLASS_KEY"; /** * User data to store the {@link TClass#isPolyfill() polyfill} property of a {@link TClass} in the index without * resolving the actual proxy. Used by N4JS validation. Could also be used to show other icons. Note: Only * {@link IEObjectDescription object descriptions} representing a {@link TClass} have this user data. */ public static final String POLYFILL_KEY = "POLYFILL_KEY"; /** * Additional user data for storing the {@link TClass#isExported() exported} property in the index. Used by test * discovery helper. If the class is not exported this key could be missing, in other words, a class is marked as * exported if this key has an associated value and the value {@link Boolean#parseBoolean(String)} is {@code true}. */ public static final String EXPORTED_CLASS_KEY = "EXPORTED_CLASS_KEY"; /** * User data to store the {@link TClass#isStaticPolyfill() staticPolyfill} property of a {@link TClass} in the index * without resolving the actual proxy. This property distinguishes between static/non-static polyfills. A static * polyfill also has the {@link TClass#isStaticPolyfill() staticPolyfill} set to {@code true} Used by N4JS * validation. Could also be used to show other icons. Note: Only {@link IEObjectDescription object descriptions} * representing a {@link TClass} have this user data. */ public static final String STATIC_POLYFILL_KEY = "STATIC_POLYFILL_KEY"; @Inject private UserdataMapper typeUserdataMapper; @Inject private IQualifiedNameProvider qualifiedNameProvider; @Override public boolean createEObjectDescriptions(final EObject eObject, IAcceptor<IEObjectDescription> acceptor) { if (getQualifiedNameProvider() == null) return false; if (eObject instanceof TModule) { TModule module = (TModule) eObject; internalCreateEObjectDescriptionForRoot(module, acceptor); for (Type type : module.getTopLevelTypes()) { internalCreateEObjectDescription(type, acceptor); } for (TVariable variable : module.getVariables()) { internalCreateEObjectDescription(variable, acceptor); } } // export is only possible for top-level elements return false; } private void internalCreateEObjectDescriptionForRoot(final TModule module, IAcceptor<IEObjectDescription> acceptor) { // user data: serialized representation final Map<String, String> userData = createUserData(module); QualifiedName qualifiedName = qualifiedNameProvider.getFullyQualifiedName(module); IEObjectDescription eod = new EObjectDescription(qualifiedName, module, userData); acceptor.accept(eod); } private Map<String, String> createUserData(final TModule module) { if (module.isPreLinkingPhase()) { return getTypeUserdataMapper().createTimestampUserData(module); } return new ForwardingMap<String, String>() { private Map<String, String> delegate; @Override protected Map<String, String> delegate() { if (delegate == null) { try { delegate = getTypeUserdataMapper().createUserData(module); } catch (Exception e) { throw new IllegalStateException(e); } } return delegate; } }; } /** * Create EObjectDescriptions for elements for which N4JSQualifiedNameProvider provides a FQN; elements with a FQN * of <code>null</code> will be ignored. */ private void internalCreateEObjectDescription(Type type, IAcceptor<IEObjectDescription> acceptor) { final String exportedName = type.getExportedName(); final String typeName = exportedName != null ? exportedName : type.getName(); if (typeName != null && typeName.length() != 0) { QualifiedName qualifiedName = qualifiedNameProvider.getFullyQualifiedName(type); if (qualifiedName != null) { // e.g. non-exported declared functions will return null for FQN Map<String, String> userData = Collections.singletonMap(ACCESS_MODIFIERY_KEY, String.valueOf(type.getTypeAccessModifier().ordinal())); // Add additional user data for descriptions representing a TClass if (type instanceof TClass) { final TClass tClass = (TClass) type; userData = newHashMap(userData); if (tClass.isExported()) { userData.put(EXPORTED_CLASS_KEY, Boolean.toString(tClass.isExported())); } userData.put(ABSTRACT_KEY, Boolean.toString(tClass.isAbstract())); userData.put(FINAL_KEY, Boolean.toString(tClass.isFinal())); userData.put(POLYFILL_KEY, Boolean.toString(tClass.isPolyfill())); userData.put(STATIC_POLYFILL_KEY, Boolean.toString(tClass.isStaticPolyfill())); userData.put(TEST_CLASS_KEY, Boolean.toString(tClass.getOwnedMembers().stream().filter(m -> m instanceof TMethod) .anyMatch(m -> AnnotationDefinition.TEST_METHOD.hasAnnotation(m)))); } IEObjectDescription eod = EObjectDescription.create(qualifiedName, type, userData); acceptor.accept(eod); } } } /** * Create EObjectDescriptions for variables for which N4JSQualifiedNameProvider provides a FQN; variables with a FQN * of <code>null</code> (currently all non-exported variables) will be ignored. */ private void internalCreateEObjectDescription(TVariable type, IAcceptor<IEObjectDescription> acceptor) { QualifiedName qualifiedName = qualifiedNameProvider.getFullyQualifiedName(type); if (qualifiedName != null) { // e.g. non-exported variables will return null for FQN IEObjectDescription eod = EObjectDescription.create(qualifiedName, type); acceptor.accept(eod); } } private UserdataMapper getTypeUserdataMapper() { return typeUserdataMapper; } }