| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package org.apache.royale.compiler.internal.definitions; |
| |
| import static org.apache.royale.compiler.common.DependencyType.EXPRESSION; |
| import static org.apache.royale.compiler.common.DependencyType.INHERITANCE; |
| import static org.apache.royale.compiler.common.DependencyType.SIGNATURE; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| import org.apache.royale.abc.semantics.Name; |
| import org.apache.royale.compiler.common.ISourceLocation; |
| import org.apache.royale.compiler.constants.IASKeywordConstants; |
| import org.apache.royale.compiler.constants.IASLanguageConstants; |
| import org.apache.royale.compiler.constants.IMetaAttributeConstants; |
| import org.apache.royale.compiler.constants.IASLanguageConstants.BuiltinType; |
| import org.apache.royale.compiler.definitions.IAccessorDefinition; |
| import org.apache.royale.compiler.definitions.IClassDefinition; |
| import org.apache.royale.compiler.definitions.IDefinition; |
| import org.apache.royale.compiler.definitions.IEffectDefinition; |
| import org.apache.royale.compiler.definitions.IEventDefinition; |
| import org.apache.royale.compiler.definitions.IFunctionDefinition; |
| import org.apache.royale.compiler.definitions.IGetterDefinition; |
| import org.apache.royale.compiler.definitions.INamespaceDefinition; |
| import org.apache.royale.compiler.definitions.ISetterDefinition; |
| import org.apache.royale.compiler.definitions.IStyleDefinition; |
| import org.apache.royale.compiler.definitions.ITypeDefinition; |
| import org.apache.royale.compiler.definitions.IVariableDefinition; |
| import org.apache.royale.compiler.definitions.metadata.IMetaTag; |
| import org.apache.royale.compiler.definitions.metadata.IMetaTagAttribute; |
| import org.apache.royale.compiler.definitions.references.INamespaceReference; |
| import org.apache.royale.compiler.definitions.references.IReference; |
| import org.apache.royale.compiler.definitions.references.IResolvedQualifiersReference; |
| import org.apache.royale.compiler.definitions.references.ReferenceFactory; |
| import org.apache.royale.compiler.internal.config.QNameNormalization; |
| import org.apache.royale.compiler.internal.definitions.metadata.MetaTag; |
| import org.apache.royale.compiler.internal.embedding.EmbedData; |
| import org.apache.royale.compiler.internal.projects.CompilerProject; |
| import org.apache.royale.compiler.internal.scopes.ASFileScope; |
| import org.apache.royale.compiler.internal.scopes.ASScope; |
| import org.apache.royale.compiler.internal.scopes.TypeScope; |
| import org.apache.royale.compiler.internal.units.EmbedCompilationUnitFactory; |
| import org.apache.royale.compiler.mxml.IMXMLLanguageConstants; |
| import org.apache.royale.compiler.problems.DuplicateSkinStateProblem; |
| import org.apache.royale.compiler.problems.EmbedMultipleMetaTagsProblem; |
| import org.apache.royale.compiler.problems.HostComponentClassNotFoundProblem; |
| import org.apache.royale.compiler.problems.HostComponentMustHaveTypeProblem; |
| import org.apache.royale.compiler.problems.ICompilerProblem; |
| import org.apache.royale.compiler.problems.MissingSkinPartProblem; |
| import org.apache.royale.compiler.problems.MissingSkinStateProblem; |
| import org.apache.royale.compiler.problems.OnlyOneHostComponentAllowedProblem; |
| import org.apache.royale.compiler.problems.SkinPartsMustBePublicProblem; |
| import org.apache.royale.compiler.problems.UnimplementedAbstractMethodProblem; |
| import org.apache.royale.compiler.problems.WrongSkinPartProblem; |
| import org.apache.royale.compiler.projects.ICompilerProject; |
| import org.apache.royale.compiler.scopes.IASScope; |
| import org.apache.royale.compiler.scopes.IDefinitionSet; |
| import org.apache.royale.compiler.tree.metadata.IEventTagNode; |
| import org.apache.royale.compiler.tree.metadata.IMetaTagNode; |
| import org.apache.royale.compiler.tree.metadata.IStyleTagNode; |
| import org.apache.royale.compiler.units.ICompilationUnit; |
| import org.apache.royale.compiler.workspaces.IWorkspace; |
| |
| import com.google.common.base.Splitter; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| |
| /** |
| * Each instance of this class represents the definition of an ActionScript |
| * class in the symbol table. |
| * <p> |
| * After a class definition is in the symbol table, it should always be accessed |
| * through the read-only <code>IClassDefinition</code> interface. |
| */ |
| public class ClassDefinition extends ClassDefinitionBase implements IClassDefinition |
| { |
| // The ClassDefinitions for *, Null, Undefined, and void are implicit; |
| // unlike for, say, Object, these are not read from any SWC. |
| // They must be created by the compiler itself. |
| private static final ClassDefinition ANY_TYPE; |
| private static final ClassDefinition NULL; |
| private static final ClassDefinition UNDEFINED; |
| private static final ClassDefinition VOID; |
| |
| // Sentinel value used internally by this class to indicate that |
| // the ABC class definition does not have [RemoteClass] meta-data. |
| private static final String NO_REMOTE_CLASS_ALIAS = ""; |
| |
| private static final String HOST_COMPONENT = "hostComponent"; |
| |
| // this gets replaced in some projects |
| public static String Event = IASLanguageConstants.Event; |
| |
| private static ClassDefinition makeImplicitClassDefinition(String name) |
| { |
| ClassDefinition def = new ClassDefinition(name, NamespaceDefinition.getPublicNamespaceDefinition()); |
| def.setPublic(); |
| def.setFinal(); |
| def.setImplicit(); |
| ASScope scope = new TypeScope(null, def); |
| scope.setContainingDefinition(def); |
| def.setContainedScope(scope); |
| return def; |
| } |
| |
| /** |
| * Private class that bundles up information extracted from [Frame] |
| * meta-data tags. |
| */ |
| private static class FrameInformation |
| { |
| IResolvedQualifiersReference factoryClass; |
| Collection<IResolvedQualifiersReference> extraClasses; |
| } |
| |
| static |
| { |
| ANY_TYPE = makeImplicitClassDefinition(IASLanguageConstants.ANY_TYPE); |
| ANY_TYPE.setDynamic(); |
| |
| NULL = makeImplicitClassDefinition(IASLanguageConstants.Null); |
| UNDEFINED = makeImplicitClassDefinition(IASLanguageConstants.Undefined); |
| VOID = makeImplicitClassDefinition(IASLanguageConstants.void_); |
| } |
| |
| public static IClassDefinition getAnyTypeClassDefinition() |
| { |
| return ANY_TYPE; |
| } |
| |
| public static IClassDefinition getNullClassDefinition() |
| { |
| return NULL; |
| } |
| |
| public static IClassDefinition getUndefinedClassDefinition() |
| { |
| return UNDEFINED; |
| } |
| |
| public static IClassDefinition getVoidClassDefinition() |
| { |
| return VOID; |
| } |
| |
| private static String getGeneratedURIPrefix(INamespaceReference namespaceRef) |
| { |
| if (namespaceRef instanceof NamespaceDefinition.INamespaceWithPackageName) |
| return ((NamespaceDefinition.INamespaceWithPackageName)namespaceRef).getGeneratedURIPrefix(); |
| return ""; |
| } |
| |
| /** |
| * Constructs a new class definition with an auto generated protected |
| * namespace. |
| * |
| * @param name Base name of the class definition, eg: DisplayObject, not |
| * flash.display.DisplayObject. |
| * @param namespaceRef {@link INamespaceReference} qualifier. Usually this |
| * will be a {@code INamespaceDefinition.IPublicNamespaceDefinition} whose |
| * URI is a package name like flash.display. |
| */ |
| public ClassDefinition(String name, INamespaceReference namespaceRef) |
| { |
| this(name, namespaceRef, NamespaceDefinition.createProtectedNamespaceDefinition(getGeneratedURIPrefix(namespaceRef) + name)); |
| } |
| |
| /** |
| * Constructs a new class definition with an explicit protected namespace. |
| * The protected namespace is used to qualify protected instance members of |
| * the ABC class. |
| * |
| * @param name Base name of the class definition, eg: DisplayObject, not |
| * flash.display.DisplayObject. |
| * @param namespaceRef {@link INamespaceReference} qualifier. Usually this |
| * will be a {@code INamespaceDefinition.IPublicNamespaceDefinition} whose |
| * URI is a package name like flash.display. |
| * @param protectedNamespace Protected namespace that will be used to |
| * qualifier protected instance members of the ABC class. |
| */ |
| public ClassDefinition(String name, INamespaceReference namespaceRef, |
| NamespaceDefinition.IProtectedNamespaceDefinition protectedNamespace) |
| { |
| super(name); |
| assert name.indexOf('.') == -1 : "name must not be a qualified name!"; |
| |
| setNamespaceReference(namespaceRef); |
| |
| String generatedURI = getGeneratedURIPrefix(namespaceRef) + name; |
| privateNamespaceReference = |
| NamespaceDefinition.createPrivateNamespaceDefinition(generatedURI); |
| protectedNamespaceReference = protectedNamespace; |
| staticProtectedNamespaceReference = |
| NamespaceDefinition.createStaticProtectedNamespaceDefinition(generatedURI); |
| |
| contingentDefinitions = new LinkedList<IDefinition>(); |
| |
| eventDefinitions = new AtomicReference<Map<String, IEventDefinition>>(); |
| styleDefinitions = new AtomicReference<Map<String, IStyleDefinition>>(); |
| effectDefinitions = new AtomicReference<Map<String, IEffectDefinition>>(); |
| |
| skinStates = new AtomicReference<String[]>(); |
| skinParts = new AtomicReference<IMetaTag[]>(); |
| |
| stateNames = new HashSet<String>(); |
| |
| frameInformation = new AtomicReference<FrameInformation>(); |
| |
| remoteClassAlias = new AtomicReference<String>(); |
| } |
| |
| private final NamespaceDefinition.ILanguageNamespaceDefinition privateNamespaceReference; |
| private final NamespaceDefinition.ILanguageNamespaceDefinition protectedNamespaceReference; |
| private final NamespaceDefinition.ILanguageNamespaceDefinition staticProtectedNamespaceReference; |
| |
| // A reference to the class that this class extends. |
| private IReference baseClassReference; |
| |
| // References to the interfaces that this class implements. |
| private IReference[] interfaceReferences; |
| |
| // The definition for the constructor of this class. |
| // This can be stored as a definition rather than as a reference |
| // because the definition is within this class |
| // rather than in a different class. |
| private IFunctionDefinition constructor; |
| |
| // This map has keys which are event names (such as "click") |
| // and values which are EventDefinitions. |
| // It contains event definitions created from [Event(...)] metadata |
| // on this class, but not from such metadata on superclasses. |
| // It is lazily created so that if no one asks for the events of a class, |
| // the EventDefinitions don't get created; for example, if we are simply |
| // compiling ActionScript files, their event metadata is irrelevant. |
| // We keep an atomic reference to it because multiple threads |
| // need to be able to lazily update this ClassDefinition after it |
| // is in a scope. |
| private AtomicReference<Map<String, IEventDefinition>> eventDefinitions; |
| |
| // This map has keys which are style names (such as "fontSize") |
| // and values which are StyleDefinitions. |
| // It contains style definitions created from [Style(...)] metadata |
| // on this class, but not from such metadata on superclasses. |
| // It is lazily created so that if no one asks for the styles of a class, |
| // the StyleDefinitions don't get created; for example, if we are simply |
| // compiling ActionScript files, their style metadata is irrelevant. |
| // We keep an atomic reference to it because multiple threads |
| // need to be able to lazily update this ClassDefinition after it |
| // is in a scope. |
| private final AtomicReference<Map<String, IStyleDefinition>> styleDefinitions; |
| |
| private final AtomicReference<Map<String, IEffectDefinition>> effectDefinitions; |
| |
| private final AtomicReference<String[]> skinStates; |
| |
| private final AtomicReference<IMetaTag[]> skinParts; |
| |
| private Set<String> stateNames; |
| |
| private final AtomicReference<FrameInformation> frameInformation; |
| |
| // Remote class alias for this class. Set from [RemoteClass] meta-data. |
| private final AtomicReference<String> remoteClassAlias; |
| |
| /** |
| * If this ClassDefinition is for an MXML class, this field is a set of |
| * implicit imports determined by the MXML instance tags that are used |
| * inside the class. |
| */ |
| private Set<String> implicitImports; |
| |
| private List<IDefinition> contingentDefinitions; |
| |
| @Override |
| public ClassClassification getClassClassification() |
| { |
| if (inPackageNamespace()) |
| return ClassClassification.PACKAGE_MEMBER; |
| return ClassClassification.FILE_MEMBER; |
| } |
| |
| @Override |
| public IFunctionDefinition getConstructor() |
| { |
| return constructor; |
| } |
| |
| protected void setConstructor(IFunctionDefinition constructor) |
| { |
| this.constructor = constructor; |
| |
| if(this.constructor.isPrivate() |
| && this.constructor.getMetaTagByName(IMetaAttributeConstants.ATTRIBUTE_PRIVATE_CONSTRUCTOR) == null) |
| { |
| // ensures that the constructor remains private when compiled into |
| // a library because the metadata is how private constructors are |
| // stored in the bytecode |
| MetaTag privateMetaTag = new MetaTag(this, IMetaAttributeConstants.ATTRIBUTE_PRIVATE_CONSTRUCTOR, new IMetaTagAttribute[0]); |
| addMetaTag(privateMetaTag); |
| } |
| } |
| |
| @Override |
| public String getBaseClassAsDisplayString() |
| { |
| return baseClassReference != null ? baseClassReference.getDisplayString() : ""; |
| } |
| |
| @Override |
| public IReference getBaseClassReference() |
| { |
| return baseClassReference; |
| } |
| |
| /** |
| * Sets a reference to the base class for this class. |
| * |
| * @param baseClassReference An {@link IReference} referring to the base |
| * class. |
| */ |
| public void setBaseClassReference(IReference baseClassReference) |
| { |
| if (baseClassReference == null) |
| { |
| // The baseClassReference of Object is null. |
| // The baseClassReference of other classes is a reference to Object, |
| // until setBaseClass() gets called (which it may never be |
| // in the case of a class declaration without an extends clause). |
| boolean isObject = getBaseName().equals(IASLanguageConstants.Object) && |
| getNamespaceReference().isPublicOrInternalNamespace() && |
| ((INamespaceDefinition)getNamespaceReference()).getURI().isEmpty(); |
| if (!isObject) |
| baseClassReference = ReferenceFactory.builtinReference(BuiltinType.OBJECT); |
| } |
| |
| this.baseClassReference = baseClassReference; |
| } |
| |
| @Override |
| public IClassDefinition resolveBaseClass(ICompilerProject project) |
| { |
| // Object has no base class. |
| if (baseClassReference == null) |
| return null; |
| |
| // Resolve the base class reference within the project, |
| // and establish an inheritance dependency. |
| ITypeDefinition typeDefinition = |
| resolveType(baseClassReference, project, INHERITANCE); |
| |
| if (typeDefinition == null) |
| return null; |
| |
| // A class cannot extend an interface. |
| if (!(typeDefinition instanceof IClassDefinition)) |
| return null; |
| |
| // The base class cannot be final. |
| if (((IClassDefinition)typeDefinition).isFinal()) |
| return null; |
| |
| // A class cannot extend itself. |
| if (typeDefinition == this) |
| return null; |
| |
| return (IClassDefinition)typeDefinition; |
| } |
| |
| @Override |
| public String[] getImplementedInterfacesAsDisplayStrings() |
| { |
| if (interfaceReferences == null) |
| return new String[0]; |
| |
| int n = interfaceReferences.length; |
| String[] interfaces = new String[n]; |
| for (int i = 0; i < n; i++) |
| interfaces[i] = interfaceReferences[i].getDisplayString(); |
| |
| return interfaces; |
| } |
| |
| public void setImplementedInterfaceReferences(IReference[] interfaceReferences) |
| { |
| this.interfaceReferences = interfaceReferences; |
| } |
| |
| @Override |
| public IReference[] getImplementedInterfaceReferences() |
| { |
| if (interfaceReferences == null) |
| return new IReference[0]; |
| |
| return interfaceReferences; |
| } |
| |
| @Override |
| public IEventDefinition getEventDefinition(IWorkspace w, String name) |
| { |
| if (eventDefinitions.get() == null) |
| buildEventDefinitions(w); |
| |
| return eventDefinitions.get().get(name); |
| } |
| |
| @Override |
| public IEventDefinition[] getEventDefinitions(IWorkspace w) |
| { |
| if (eventDefinitions.get() == null) |
| buildEventDefinitions(w); |
| |
| return eventDefinitions.get().values().toArray(new IEventDefinition[0]); |
| } |
| |
| /* |
| * Lazily creates a map of event names to event definitions from the event |
| * metadata declared on this class. |
| */ |
| private void buildEventDefinitions(IWorkspace w) |
| { |
| Map<String, IEventDefinition> map = new HashMap<String, IEventDefinition>(); |
| |
| Map<String, IEventTagNode> availableTagNodes = new HashMap<String, IEventTagNode>(); |
| |
| // required because of CMP-2168 : can't get the event definition from the metatag. |
| if (getNode() != null && getNode().getMetaTags() != null) |
| { |
| IMetaTagNode[] allNodes = this.getNode().getMetaTags().getTagsByName(IMetaAttributeConstants.ATTRIBUTE_EVENT); |
| // eventDefinition.setNode((IDefinitionNode)metaTag.getTagNode()); |
| for (IMetaTagNode iMetaTagNode : allNodes) |
| { |
| IEventTagNode eventTagNode = (IEventTagNode)iMetaTagNode; |
| availableTagNodes.put(eventTagNode.getName(), (IEventTagNode)iMetaTagNode); |
| } |
| } |
| |
| IMetaTag[] metaTags = getAllMetaTags(); |
| if (metaTags != null) |
| { |
| for (IMetaTag metaTag : metaTags) |
| { |
| if (metaTag.getTagName().equals(IMetaAttributeConstants.ATTRIBUTE_EVENT)) |
| { |
| // Most event metadata looks like this: |
| // [Event(name="click", type="flash.events.MouseEvent")] |
| String name = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_EVENT_NAME); |
| String type = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_EVENT_TYPE); |
| |
| // However, |
| // [Event(name="click")] |
| // and |
| // [Event("click")] |
| // are also allowed. |
| if (name == null) |
| { |
| IMetaTagAttribute[] attrs = metaTag.getAllAttributes(); |
| if (attrs != null && attrs.length > 0) |
| { |
| name = attrs[0].getValue(); |
| } |
| } |
| if (type == null) |
| type = ClassDefinition.Event; |
| |
| if (name != null) |
| { |
| EventDefinition eventDefinition = new EventDefinition(name, this); |
| |
| // This scope is necessary for resolving the event's type. |
| eventDefinition.setContainingScope(getContainingScope()); |
| |
| eventDefinition.setTypeReference(ReferenceFactory.packageQualifiedReference(w, type)); |
| |
| if (availableTagNodes.containsKey(name)) |
| { |
| eventDefinition.setNode(availableTagNodes.get(name)); |
| } |
| map.put(name, eventDefinition); |
| } |
| } |
| } |
| } |
| |
| // Let the first thread that builds the map atomically set it. |
| eventDefinitions.compareAndSet(null, map); |
| } |
| |
| @Override |
| public IEventDefinition[] findEventDefinitions(ICompilerProject project) |
| { |
| IWorkspace workspace = project.getWorkspace(); |
| |
| Map<String, IEventDefinition> map = new HashMap<String, IEventDefinition>(); |
| |
| // Iterate over this class and its superclasses. |
| for (IClassDefinition c : classIterable(project, true)) |
| { |
| for (IEventDefinition eventTag : c.getEventDefinitions(workspace)) |
| { |
| String eventName = eventTag.getBaseName(); |
| |
| // By checking whether the event is already in the map, |
| // we can make sure that an event definition with a particular name |
| // on a subclass overrides ones with the same name on superclasses. |
| if (!map.containsKey(eventName)) |
| map.put(eventName, eventTag); |
| } |
| } |
| |
| return map.values().toArray(new IEventDefinition[0]); |
| } |
| |
| @Override |
| public IStyleDefinition getStyleDefinition(IWorkspace workspace, String name) |
| { |
| if (styleDefinitions.get() == null) |
| buildStyleDefinitions(workspace); |
| |
| return styleDefinitions.get().get(name); |
| } |
| |
| @Override |
| public IStyleDefinition[] getStyleDefinitions(IWorkspace w) |
| { |
| if (styleDefinitions.get() == null) |
| buildStyleDefinitions(w); |
| |
| return styleDefinitions.get().values().toArray(new IStyleDefinition[0]); |
| } |
| |
| /* |
| * Lazily creates a map of style names to style definitions from the style |
| * metadata declared on this class. |
| */ |
| private void buildStyleDefinitions(IWorkspace workspace) |
| { |
| Map<String, IStyleDefinition> styleMap = new HashMap<String, IStyleDefinition>(); |
| |
| for (IMetaTag metaTag : getAllMetaTags()) |
| { |
| if (metaTag.getTagName().equals(IMetaAttributeConstants.ATTRIBUTE_STYLE)) |
| { |
| String name = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_NAME); |
| String type = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_TYPE); |
| String format = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_FORMAT); |
| String inherit = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_INHERIT); |
| String themes = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_THEME); |
| String minValue = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_MIN_VALUE); |
| String minValueExclusive = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_MIN_VALUE_EXCLUSIVE); |
| String maxValue = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_MAX_VALUE); |
| String maxValueExclusive = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_MAX_VALUE_EXCLUSIVE); |
| String enumeration = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_ENUMERATION); |
| String states = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_STATES); |
| String arrayTypeName = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_ARRAYTYPE); |
| |
| if (name == null) // If there is no Name, we can't create a definition, nor should we try |
| continue; |
| if (type == null) // missing type is perfectly legal. so let's patch |
| // it up to something that will resolve correctly for us. |
| type = "*"; |
| |
| StyleDefinition styleDefinition = new StyleDefinition(name, this); |
| |
| // This scope is necessary for resolving the style's type. |
| styleDefinition.setContainingScope(getContainingScope()); |
| |
| styleDefinition.setTypeReference(ReferenceFactory.packageQualifiedReference(workspace, type)); |
| styleDefinition.setFormat(format); |
| styleDefinition.setInherit(inherit); |
| styleDefinition.setThemes(themes); |
| styleDefinition.setMinValue(minValue); |
| styleDefinition.setMinValueExclusive(minValueExclusive); |
| styleDefinition.setMaxValue(maxValue); |
| styleDefinition.setMaxValueExclusive(maxValueExclusive); |
| styleDefinition.setArrayType(arrayTypeName); |
| if (states != null) |
| { |
| final Iterable<String> statesIterable = Splitter.on(",") |
| .omitEmptyStrings() |
| .trimResults() |
| .split(states); |
| final String[] statesArray = Iterables.toArray(statesIterable, String.class); |
| styleDefinition.setStates(statesArray); |
| } |
| if (enumeration != null) |
| { |
| // enumerations are comma seprated, with no spaces |
| String[] split = enumeration.split(","); |
| styleDefinition.setEnumeration(split); |
| } |
| IMetaTagNode styleTagNode = metaTag.getTagNode(); |
| if (styleTagNode != null && styleTagNode instanceof IStyleTagNode) |
| { |
| styleDefinition.setNode((IStyleTagNode)styleTagNode); |
| } |
| styleMap.put(name, styleDefinition); |
| } |
| } |
| |
| // Let the first thread that builds the map atomically set it. |
| styleDefinitions.compareAndSet(null, styleMap); |
| } |
| |
| @Override |
| public IStyleDefinition[] findStyleDefinitions(ICompilerProject project) |
| { |
| IWorkspace workspace = project.getWorkspace(); |
| |
| Map<String, IStyleDefinition> map = new HashMap<String, IStyleDefinition>(); |
| |
| // Iterate over this class and its superclasses. |
| for (IClassDefinition c : classIterable(project, true)) |
| { |
| for (IStyleDefinition styleTag : c.getStyleDefinitions(workspace)) |
| { |
| String styleName = styleTag.getBaseName(); |
| |
| // By checking whether the style is already in the map, |
| // we can make sure that a style definition with a particular name |
| // on a subclass overrides ones with the same name on superclasses. |
| if (!map.containsKey(styleName)) |
| map.put(styleName, styleTag); |
| } |
| } |
| |
| return map.values().toArray(new IStyleDefinition[0]); |
| } |
| |
| @Override |
| public IEffectDefinition getEffectDefinition(IWorkspace w, String name) |
| { |
| if (effectDefinitions.get() == null) |
| buildEffectDefinitions(w); |
| |
| return effectDefinitions.get().get(name); |
| } |
| |
| @Override |
| public IEffectDefinition[] getEffectDefinitions(IWorkspace w) |
| { |
| if (effectDefinitions.get() == null) |
| buildEffectDefinitions(w); |
| |
| return effectDefinitions.get().values().toArray(new IEffectDefinition[0]); |
| } |
| |
| /* |
| * Lazily creates a map of effect names to effect definitions from the |
| * effect metadata declared on this class. |
| */ |
| private void buildEffectDefinitions(IWorkspace workspace) |
| { |
| Map<String, IEffectDefinition> effectMap = new HashMap<String, IEffectDefinition>(); |
| |
| for (IMetaTag metaTag : getAllMetaTags()) |
| { |
| if (metaTag.getTagName().equals(IMetaAttributeConstants.ATTRIBUTE_EFFECT)) |
| { |
| String name = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_EFFECT_NAME); |
| String eventName = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_EFFECT_EVENT); |
| |
| EffectDefinition effectDefinition = new EffectDefinition(name, this); |
| effectDefinition.setEvent(eventName); |
| // Set the effect definition containing scope, so that clients |
| // can find which file scope contains the effect definition. |
| effectDefinition.setContainingScope(getContainingScope()); |
| |
| effectMap.put(name, effectDefinition); |
| } |
| } |
| |
| // Let the first thread that builds the map atomically set it. |
| effectDefinitions.compareAndSet(null, effectMap); |
| } |
| |
| @Override |
| public IEffectDefinition[] findEffectDefinitions(ICompilerProject project) |
| { |
| IWorkspace workspace = project.getWorkspace(); |
| |
| Map<String, IEffectDefinition> map = new HashMap<String, IEffectDefinition>(); |
| |
| // Iterate over this class and its superclasses. |
| for (IClassDefinition c : classIterable(project, true)) |
| { |
| for (IEffectDefinition effectTag : c.getEffectDefinitions(workspace)) |
| { |
| String effectName = effectTag.getBaseName(); |
| |
| // By checking whether the effect is already in the map, |
| // we can make sure that an effect definition with a particular name |
| // on a subclass overrides ones with the same name on superclasses. |
| if (!map.containsKey(effectName)) |
| map.put(effectName, effectTag); |
| } |
| } |
| |
| return map.values().toArray(new IEffectDefinition[0]); |
| } |
| |
| /* |
| * Lazily creates a array of skin state names from the skin state metadata |
| * declared on this class. |
| */ |
| private void buildSkinStates(Collection<ICompilerProblem> problems) |
| { |
| IMetaTag[] tags = getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_SKIN_STATE); |
| |
| Set<String> states = new HashSet<String>(); |
| |
| for (IMetaTag tag : tags) |
| { |
| IMetaTagAttribute[] attributes = tag.getAllAttributes(); |
| // old compiler only ever looked at first value, and ignored |
| // anything else, so match that behavior here. |
| if (attributes.length >= 1) |
| { |
| String state = attributes[0].getValue(); |
| if (states.contains(state)) |
| { |
| ICompilerProblem problem = new DuplicateSkinStateProblem(tag, state); |
| problems.add(problem); |
| } |
| states.add(state); |
| } |
| } |
| |
| // Let the first thread that builds the array atomically set it. |
| skinStates.compareAndSet(null, states.toArray(new String[states.size()])); |
| } |
| |
| @Override |
| public String[] getSkinStates(Collection<ICompilerProblem> problems) |
| { |
| if (skinStates.get() == null) |
| buildSkinStates(problems); |
| |
| return skinStates.get(); |
| } |
| |
| @Override |
| public String[] findSkinStates(ICompilerProject project, Collection<ICompilerProblem> problems) |
| { |
| Set<String> states = new HashSet<String>(); |
| |
| // Iterate over the superclasses first, but don't worry about duplicates |
| // yet. The superclasses will find their own duplicates! |
| for (IClassDefinition c : classIterable(project, false)) |
| { |
| for (String state : c.getSkinStates(problems)) |
| { |
| states.add(state); |
| } |
| } |
| // Then, add the states from this class and check for duplicates that |
| // specifically come from this class |
| for (String state : getSkinStates(problems)) |
| { |
| if (states.contains(state)) |
| { |
| ICompilerProblem problem = new DuplicateSkinStateProblem(this, state); |
| problems.add(problem); |
| } |
| |
| states.add(state); |
| } |
| |
| return states.toArray(new String[states.size()]); |
| } |
| |
| /** |
| * Get any embedded data decorating the class definition |
| * |
| * @param project Project to resolve against |
| * @param problems Any problems resolving the embedded data |
| * @return EmbedData or null if no embedded data, or a problem resolving |
| * embedded data |
| */ |
| public EmbedData getEmbeddedAsset(CompilerProject project, Collection<ICompilerProblem> problems) |
| { |
| IMetaTag[] embedMetaTags = getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_EMBED); |
| if (embedMetaTags == null || embedMetaTags.length == 0) |
| return null; |
| |
| if (embedMetaTags.length > 1) |
| { |
| problems.add(new EmbedMultipleMetaTagsProblem(getNode())); |
| return null; |
| } |
| |
| String containingSourceFilename = getFileSpecification().getPath(); |
| ISourceLocation location = embedMetaTags[0]; |
| IMetaTagAttribute[] attributes = embedMetaTags[0].getAllAttributes(); |
| |
| return EmbedCompilationUnitFactory.getEmbedData(project, getQualifiedName(), containingSourceFilename, location, attributes, problems); |
| } |
| |
| private Collection<VariableDefinition> getAllLocalVariables() |
| { |
| Collection<IDefinitionSet> allDefinitions = getContainedScope().getAllLocalDefinitionSets(); |
| List<VariableDefinition> variables = new LinkedList<VariableDefinition>(); |
| |
| for (IDefinitionSet defSet : allDefinitions) |
| { |
| for (int i = 0; i < defSet.getSize(); ++i) |
| { |
| IDefinition definition = defSet.getDefinition(i); |
| if (definition instanceof VariableDefinition) |
| variables.add((VariableDefinition)definition); |
| } |
| } |
| |
| return variables; |
| } |
| |
| private Collection<GetterDefinition> getAllLocalGetters() |
| { |
| Collection<IDefinitionSet> allDefinitions = getContainedScope().getAllLocalDefinitionSets(); |
| List<GetterDefinition> getters = new LinkedList<GetterDefinition>(); |
| |
| for (IDefinitionSet defSet : allDefinitions) |
| { |
| for (int i = 0; i < defSet.getSize(); ++i) |
| { |
| IDefinition definition = defSet.getDefinition(i); |
| if (definition instanceof GetterDefinition) |
| getters.add((GetterDefinition)definition); |
| } |
| } |
| |
| return getters; |
| } |
| |
| /* |
| * Lazily creates a array of skin parts from the skin part metadata declared |
| * on this class. |
| */ |
| private void buildSkinParts(Collection<ICompilerProblem> problems) |
| { |
| Collection<VariableDefinition> variables = getAllLocalVariables(); |
| |
| List<IMetaTag> parts = new LinkedList<IMetaTag>(); |
| for (VariableDefinition variable : variables) |
| { |
| IMetaTag skinPart = variable.getSkinPart(); |
| if (skinPart != null) |
| parts.add(skinPart); |
| } |
| |
| Collection<GetterDefinition> getters = getAllLocalGetters(); |
| for (GetterDefinition getter : getters) |
| { |
| IMetaTag skinPart = getter.getSkinPart(); |
| if (skinPart != null) |
| parts.add(skinPart); |
| } |
| |
| for (IMetaTag part : parts) |
| { |
| IDefinition definition = part.getDecoratedDefinition(); |
| if (!definition.getNamespaceReference().equals(NamespaceDefinition.getPublicNamespaceDefinition())) |
| { |
| ICompilerProblem problem = new SkinPartsMustBePublicProblem(definition); |
| problems.add(problem); |
| } |
| } |
| |
| // Let the first thread that builds the array atomically set it. |
| skinParts.compareAndSet(null, parts.toArray(new IMetaTag[parts.size()])); |
| } |
| |
| @Override |
| public IMetaTag[] getSkinParts(Collection<ICompilerProblem> problems) |
| { |
| if (skinParts.get() == null) |
| buildSkinParts(problems); |
| |
| return skinParts.get(); |
| } |
| |
| @Override |
| public IMetaTag[] findSkinParts(ICompilerProject project, Collection<ICompilerProblem> problems) |
| { |
| Map<String, IMetaTag> map = new HashMap<String, IMetaTag>(); |
| |
| // Iterate over this class and its superclasses. |
| for (IClassDefinition c : classIterable(project, true)) |
| { |
| for (IMetaTag skinPart : c.getSkinParts(problems)) |
| { |
| String variableName = skinPart.getDecoratedDefinition().getBaseName(); |
| |
| // By checking whether the variable is already in the map, |
| // we can make sure that a variable definition with a particular name |
| // on a subclass overrides ones with the same name on superclasses. |
| if (!map.containsKey(variableName)) |
| map.put(variableName, skinPart); |
| } |
| } |
| |
| return map.values().toArray(new IMetaTag[map.size()]); |
| } |
| |
| /** |
| * Add a state name to the class |
| * |
| * @param stateName The name to add |
| */ |
| public void addStateName(String stateName) |
| { |
| stateNames.add(stateName); |
| } |
| |
| @Override |
| public Set<String> getStateNames() |
| { |
| return stateNames; |
| } |
| |
| @Override |
| public Set<String> findStateNames(ICompilerProject project) |
| { |
| Set<String> set = new HashSet<String>(); |
| |
| // Iterate over this class and its superclasses. |
| for (IClassDefinition c : classIterable(project, true)) |
| { |
| set.addAll(c.getStateNames()); |
| } |
| |
| return set; |
| } |
| |
| public List<IDefinition> getContingentDefinitions() |
| { |
| return contingentDefinitions; |
| } |
| |
| /** |
| * Build any contingent definitions that may be required for the class such |
| * as hostComponent. These definitions will only be resolved against or |
| * codegend if a superclass does not implement the definition already. Note |
| * that this should only be called when constructring classes derived from |
| * AS or MXML. It is not needed when creating ABC sourced classes. |
| */ |
| public void buildContingentDefinitions() |
| { |
| // for now, only host components need to be created |
| IDefinition definition = buildHostComponentMember(); |
| if (definition != null) |
| contingentDefinitions.add(definition); |
| } |
| |
| /** |
| * If there is host component meta data on the class, run semantic checks on |
| * it. |
| * |
| * @param project The compiler project. |
| * @param problems The collection of compiler problems to which this method should add problems. |
| */ |
| public void verifyHostComponent(CompilerProject project, Collection<ICompilerProblem> problems) |
| { |
| IMetaTag[] metaTags = getHostComponentMetaData(); |
| if (metaTags.length == 0) |
| return; |
| |
| if (metaTags.length > 1) |
| { |
| ICompilerProblem problem = new OnlyOneHostComponentAllowedProblem(metaTags[1]); |
| problems.add(problem); |
| } |
| |
| String hostComponentClassName = getHostComponentClassName(metaTags[0]); |
| if (hostComponentClassName == null) |
| { |
| ICompilerProblem problem = new HostComponentMustHaveTypeProblem(metaTags[0]); |
| problems.add(problem); |
| // without a host component name, there aren't any more checks we |
| // can do, so just bail out. |
| return; |
| } |
| |
| IResolvedQualifiersReference hostComponentRef = ReferenceFactory.packageQualifiedReference(project.getWorkspace(), hostComponentClassName); |
| ICompilationUnit referencingCU = project.getScope().getCompilationUnitForDefinition(this); |
| IDefinition hostComponentDef = hostComponentRef.resolve(project, referencingCU, SIGNATURE); |
| if (!(hostComponentDef instanceof IClassDefinition)) |
| { |
| ICompilerProblem problem = new HostComponentClassNotFoundProblem(metaTags[0], hostComponentClassName); |
| problems.add(problem); |
| // without a host component class definition, there aren't any more checks we |
| // can do, so just bail out. |
| return; |
| } |
| |
| IClassDefinition hostComponentClassDef = (IClassDefinition)hostComponentDef; |
| verifySkinParts(hostComponentClassDef, project, problems); |
| verifySkinStates(hostComponentClassDef, project, problems); |
| } |
| |
| private void verifySkinParts(IClassDefinition hostComponentDef, ICompilerProject project, Collection<ICompilerProblem> problems) |
| { |
| IMetaTag[] skinParts = hostComponentDef.findSkinParts(project, problems); |
| |
| for (IMetaTag skinPart : skinParts) |
| { |
| IVariableDefinition hostSkinPartDef = (IVariableDefinition)skinPart.getDecoratedDefinition(); |
| |
| String hostStringPartName = hostSkinPartDef.getBaseName(); |
| IDefinition skinPartDef = getContainedScope().getPropertyFromDef(project, this, hostStringPartName, false); |
| if (skinPartDef instanceof ISetterDefinition) |
| skinPartDef = ((ISetterDefinition)skinPartDef).resolveGetter(project); |
| |
| // the skinPart definition needs to be either a variable or getter |
| if (!((skinPartDef instanceof IVariableDefinition) || (skinPartDef instanceof IGetterDefinition))) |
| skinPartDef = null; |
| |
| ITypeDefinition skinPartTypeDef = null; |
| if (skinPartDef != null) |
| skinPartTypeDef = skinPartDef.resolveType(project); |
| |
| if (skinPartTypeDef == null && hostSkinPartDef.isRequiredSkinPart()) |
| { |
| ICompilerProblem problem = new MissingSkinPartProblem(skinPart, hostComponentDef.getBaseName()); |
| problems.add(problem); |
| } |
| else if (skinPartTypeDef != null && !skinPartTypeDef.isInstanceOf(hostSkinPartDef.resolveType(project), project)) |
| { |
| ICompilerProblem problem = new WrongSkinPartProblem(skinPart, skinPartTypeDef, hostSkinPartDef.resolveType(project)); |
| problems.add(problem); |
| |
| } |
| } |
| } |
| |
| private void verifySkinStates(IClassDefinition hostComponentDef, ICompilerProject project, Collection<ICompilerProblem> problems) |
| { |
| Set<String> stateNames = findStateNames(project); |
| |
| String[] skinStates = hostComponentDef.findSkinStates(project, problems); |
| for (String skinStateName : skinStates) |
| { |
| if (!stateNames.contains(skinStateName)) |
| problems.add(new MissingSkinStateProblem(getFileSpecification().getPath(), skinStateName)); |
| } |
| } |
| |
| private VariableDefinition buildHostComponentMember() |
| { |
| // if there's no host component meta data, no need to create any contingent member |
| IMetaTag[] metaTags = getHostComponentMetaData(); |
| if (metaTags.length == 0) |
| return null; |
| |
| String hostComponentClassName = getHostComponentClassName(metaTags[0]); |
| if (hostComponentClassName == null) |
| return null; |
| |
| ASScope containedScope = getContainedScope(); |
| VariableDefinition hostComponentDef = new VariableDefinition(HOST_COMPONENT); |
| hostComponentDef.setPublic(); |
| hostComponentDef.setBindable(); |
| hostComponentDef.setImplicit(); |
| hostComponentDef.setContingent(); |
| |
| IReference typeRef = ReferenceFactory.packageQualifiedReference(containedScope.getWorkspace(), hostComponentClassName); |
| hostComponentDef.setTypeReference(typeRef); |
| |
| containedScope.addDefinition(hostComponentDef); |
| |
| return hostComponentDef; |
| } |
| |
| public VariableDefinition buildOuterDocumentMember(IReference outerClass) |
| { |
| ASScope containedScope = getContainedScope(); |
| VariableDefinition outerDocumentDef = new VariableDefinition(IMXMLLanguageConstants.PROPERTY_OUTER_DOCUMENT); |
| outerDocumentDef.setPublic(); |
| outerDocumentDef.setBindable(); |
| outerDocumentDef.setImplicit(); |
| outerDocumentDef.setTypeReference(outerClass); |
| |
| containedScope.addDefinition(outerDocumentDef); |
| |
| // the outer document isn't really a contingent definition, as the user |
| // should never define it, and we should always create one, but making |
| // use of the contingent variable is useful as we can create it here |
| // and then resolve it during codegen and create a problem if |
| // we resolved to anything but the contingent definition outerDocument. |
| outerDocumentDef.setContingent(); |
| contingentDefinitions.add(outerDocumentDef); |
| |
| return outerDocumentDef; |
| } |
| |
| private IMetaTag[] getHostComponentMetaData() |
| { |
| return getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_HOST_COMPONENT); |
| } |
| |
| private static String getHostComponentClassName(IMetaTag metaTag) |
| { |
| assert IMetaAttributeConstants.ATTRIBUTE_HOST_COMPONENT.equals(metaTag.getTagName()); |
| String hostComponentClassName = metaTag.getValue(); |
| // no component name, so don't create a contingent member |
| if (hostComponentClassName == null || hostComponentClassName.isEmpty()) |
| return null; |
| |
| return hostComponentClassName; |
| } |
| |
| private void buildFrameInformation(IWorkspace w) |
| { |
| FrameInformation result = new FrameInformation(); |
| IMetaTag[] frameTags = getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_FRAME); |
| |
| result.extraClasses = Collections.emptySet(); |
| for (IMetaTag frameTag : frameTags) |
| { |
| String frameTagFactoryClass = frameTag.getAttributeValue(IMetaAttributeConstants.NAME_FRAME_FACTORY_CLASS); |
| if (frameTagFactoryClass != null) |
| result.factoryClass = ReferenceFactory.packageQualifiedReference(w, QNameNormalization.normalize(frameTagFactoryClass)); |
| String frameTagExtraClass = frameTag.getAttributeValue(IMetaAttributeConstants.NAME_FRAME_EXTRA_CLASS); |
| if (frameTagExtraClass != null) |
| { |
| if (result.extraClasses.size() == 0) |
| result.extraClasses = new LinkedList<IResolvedQualifiersReference>(); |
| result.extraClasses.add(ReferenceFactory.packageQualifiedReference(w, QNameNormalization.normalize(frameTagExtraClass))); |
| } |
| } |
| frameInformation.compareAndSet(null, result); |
| } |
| |
| private IResolvedQualifiersReference getFactoryClass(IWorkspace w) |
| { |
| if (frameInformation.get() == null) |
| buildFrameInformation(w); |
| return frameInformation.get().factoryClass; |
| } |
| |
| public boolean hasOwnFactoryClass(IWorkspace w) |
| { |
| return getFactoryClass(w) != null; |
| } |
| |
| private void buildRemoteClassAlias() |
| { |
| IMetaTag[] remoteClassTags = getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_REMOTECLASS); |
| String remoteClassAlias = NO_REMOTE_CLASS_ALIAS; |
| if (remoteClassTags.length != 0) |
| { |
| // Reading the source for the Flex 4.5.X compiler, it seems that |
| // you can only have one alias for each class and that the last alias |
| // wins. |
| IMetaTag lastRemoteClassTag = remoteClassTags[remoteClassTags.length - 1]; |
| remoteClassAlias = lastRemoteClassTag.getAttributeValue(IMetaAttributeConstants.NAME_REMOTECLASS_ALIAS); |
| // Goofy logic copied from flex2.compiler.as3.SyntaxTreeEvaluator. |
| // Original code references a bug number 159983 in an unknown bug base. |
| if (remoteClassAlias == null) |
| remoteClassAlias = "<" + getQualifiedName(); |
| } |
| |
| this.remoteClassAlias.compareAndSet(null, remoteClassAlias); |
| } |
| |
| /** |
| * Get's the remote class alias for this class. |
| * |
| * @return The remote class alias for this class. |
| */ |
| public String getRemoteClassAlias() |
| { |
| if (remoteClassAlias.get() == null) |
| buildRemoteClassAlias(); |
| String result = remoteClassAlias.get(); |
| if (result == NO_REMOTE_CLASS_ALIAS) |
| return null; |
| return result; |
| } |
| |
| public ClassDefinition resolveInheritedFactoryClass(ICompilerProject project) |
| { |
| for (IClassDefinition c : classIterable(project, true)) |
| { |
| assert c instanceof ClassDefinition : "IClassDefinition " + c.getBaseName() + "is not a ClassDefinition!"; |
| ClassDefinition classDef = (ClassDefinition)c; |
| IResolvedQualifiersReference factoryRef = classDef.getFactoryClass(project.getWorkspace()); |
| if (factoryRef != null) |
| { |
| IDefinition factoryDef = factoryRef.resolve(project, (ASScope)this.getContainingScope(), EXPRESSION, true); |
| if (factoryDef instanceof ClassDefinition) |
| return (ClassDefinition)factoryDef; |
| } |
| } |
| return null; |
| } |
| |
| public Collection<IDefinition> resolveExtraClasses(ICompilerProject project) |
| { |
| if (frameInformation.get() == null) |
| buildFrameInformation(project.getWorkspace()); |
| LinkedList<IDefinition> result = new LinkedList<IDefinition>(); |
| for (IResolvedQualifiersReference extraClassRef : frameInformation.get().extraClasses) |
| { |
| IDefinition extraClassDef = |
| extraClassRef.resolve(project, (ASScope)this.getContainingScope(), EXPRESSION, true); |
| if (extraClassDef != null) |
| result.add(extraClassDef); |
| } |
| return ImmutableList.<IDefinition> copyOf(result); |
| } |
| |
| @Override |
| public String getDefaultPropertyName(ICompilerProject project) |
| { |
| // Look for [DefaultProperty("foo")] on this class its superclasses. |
| for (IClassDefinition c : classIterable(project, true)) |
| { |
| IMetaTag defaultPropertyMetaData = c.getMetaTagByName(IMetaAttributeConstants.ATTRIBUTE_DEFAULTPROPERTY); |
| if (defaultPropertyMetaData != null) |
| { |
| IMetaTagAttribute attrs[] = defaultPropertyMetaData.getAllAttributes(); |
| if ((attrs.length == 1) && (!attrs[0].hasKey())) |
| return attrs[0].getValue(); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public void setContainedScope(IASScope value) |
| { |
| super.setContainedScope(value); |
| ((DefinitionBase)privateNamespaceReference).setContainingScope(value); |
| } |
| |
| /** |
| * Gets the {@link INamespaceReference} that resolves to the private |
| * namespace for this {@link IClassDefinition}. |
| * |
| * @return The {@link INamespaceReference} that resolves to the private |
| * namespace for this {@link IClassDefinition}. |
| */ |
| public NamespaceDefinition.ILanguageNamespaceDefinition getPrivateNamespaceReference() |
| { |
| return privateNamespaceReference; |
| } |
| |
| /** |
| * Gets the {@link INamespaceReference} that resolves to the protected |
| * namespace for this {@link IClassDefinition}. |
| * |
| * @return The {@link INamespaceReference} that resolves to the protected |
| * namespace for this {@link IClassDefinition}. |
| */ |
| @Override |
| public NamespaceDefinition.ILanguageNamespaceDefinition getProtectedNamespaceReference() |
| { |
| return protectedNamespaceReference; |
| } |
| |
| /** |
| * Gets the {@link INamespaceReference} that resolves to the static |
| * protected namespace for this {@link IClassDefinition}. |
| * |
| * @return The {@link INamespaceReference} that resolves to the static |
| * protected namespace for this {@link IClassDefinition}. |
| */ |
| @Override |
| public NamespaceDefinition.ILanguageNamespaceDefinition getStaticProtectedNamespaceReference() |
| { |
| return staticProtectedNamespaceReference; |
| } |
| |
| @Override |
| public Name getMName(ICompilerProject project) |
| { |
| // "*" isn't referenced by name in ABC - it's usually |
| // just index 0, which is what AET does for a null name. |
| if (getBaseName() == IASLanguageConstants.ANY_TYPE) |
| return null; |
| |
| return super.getMName(project); |
| } |
| |
| @Override |
| public ASFileScope getFileScope() |
| { |
| if (isImplicit()) |
| return null; |
| else |
| return super.getFileScope(); |
| } |
| |
| /** |
| * Adds a new node to the list of nodes representing the implicit imports |
| * created by MXML tags. |
| */ |
| public void addImplicitImport(String qname) |
| { |
| if (implicitImports == null) |
| implicitImports = new HashSet<String>(); |
| |
| implicitImports.add(qname); |
| } |
| |
| /** |
| * Gets the implicit imports created by MXML tags, for use by CodeModel. |
| * |
| * @return A list of {@code MXMLImplicitImportNode} objects. |
| */ |
| public String[] getImplicitImports() |
| { |
| return implicitImports != null ? |
| implicitImports.toArray(new String[0]) : |
| new String[0]; |
| } |
| |
| /** |
| * Adds implicit variable definitions for "this" and "super" to this class |
| * definition's contained scope. These variable definitions are for code |
| * model compatibility and are not codegen'd. |
| */ |
| public void setupThisAndSuper() |
| { |
| // Create an implicit VariableDefinition for "this". |
| VariableDefinition thisDef = new VariableDefinition(IASKeywordConstants.THIS); |
| |
| ASScope containedScope = getContainedScope(); |
| IWorkspace workspace = containedScope.getWorkspace(); |
| |
| // this is a lexical ref for codemodel backwards compat |
| thisDef.setTypeReference(ReferenceFactory.lexicalReference(workspace, getBaseName())); |
| thisDef.setImplicit(); |
| thisDef.setNamespaceReference(NamespaceDefinition.getCodeModelImplicitDefinitionNamespace()); |
| |
| // Create an implicit VariableDefinition for "super". |
| VariableDefinition superDef = new VariableDefinition(IASKeywordConstants.SUPER); |
| IReference baseClassRef = getBaseClassReference(); |
| if (baseClassRef == null) |
| { |
| baseClassRef = ReferenceFactory.builtinReference(BuiltinType.OBJECT); |
| } |
| superDef.setTypeReference(baseClassRef); |
| superDef.setImplicit(); |
| superDef.setNamespaceReference(NamespaceDefinition.getCodeModelImplicitDefinitionNamespace()); |
| |
| // Add these definitions to the class scope. |
| containedScope.addDefinition(thisDef); |
| containedScope.addDefinition(superDef); |
| } |
| |
| /** |
| * Mark this class as an excluded class by adding [ExcludeClass] meta data. |
| */ |
| public void setExcludedClass() |
| { |
| MetaTag excludeClassMetaTag = new MetaTag(this, IMetaAttributeConstants.ATTRIBUTE_EXCLUDECLASS, new IMetaTagAttribute[0]); |
| addMetaTag(excludeClassMetaTag); |
| } |
| |
| /** |
| * For debugging only. Produces a string such as |
| * <code>public class B extends A implements I1, I2</code>. |
| */ |
| @Override |
| protected void buildInnerString(StringBuilder sb) |
| { |
| sb.append(getNamespaceReferenceAsString()); |
| sb.append(' '); |
| |
| sb.append(IASKeywordConstants.CLASS); |
| sb.append(' '); |
| |
| sb.append(getBaseName()); |
| sb.append(' '); |
| |
| if (baseClassReference != null) |
| { |
| sb.append(IASKeywordConstants.EXTENDS); |
| sb.append(' '); |
| |
| String baseClassName = getBaseClassAsDisplayString(); |
| sb.append(baseClassName.isEmpty() ? IASLanguageConstants.Object : baseClassName); |
| } |
| |
| String[] implementedInterfaces = getImplementedInterfacesAsDisplayStrings(); |
| int n = implementedInterfaces.length; |
| if (n > 0) |
| { |
| sb.append(' '); |
| sb.append(IASKeywordConstants.IMPLEMENTS); |
| sb.append(' '); |
| |
| for (int i = 0; i < n; i++) |
| { |
| sb.append(implementedInterfaces[i]); |
| if (i < n - 1) |
| { |
| sb.append(','); |
| sb.append(' '); |
| } |
| } |
| } |
| } |
| |
| public boolean getOwnNeedsProtected() |
| { |
| return ((TypeScope)getContainedScope()).getNeedsProtected(); |
| } |
| |
| @Override |
| public IClassDefinition resolveHostComponent(ICompilerProject project) |
| { |
| for (IClassDefinition c : classIterable(project, true)) |
| { |
| if (!(c instanceof ClassDefinition)) |
| continue; |
| |
| ClassDefinition classDef = (ClassDefinition)c; |
| IMetaTag[] hostComponentMetaData = classDef.getHostComponentMetaData(); |
| if (hostComponentMetaData.length < 1) |
| continue; |
| String hostComponentName = getHostComponentClassName(hostComponentMetaData[0]); |
| if (hostComponentName == null) |
| return null; |
| IResolvedQualifiersReference hostComponentRef = ReferenceFactory.packageQualifiedReference(project.getWorkspace(), hostComponentName); |
| IDefinition hostComponentDef = hostComponentRef.resolve(project); |
| if (!(hostComponentDef instanceof IClassDefinition)) |
| return null; |
| return (IClassDefinition)hostComponentDef; |
| } |
| return null; |
| } |
| |
| @Override |
| public boolean isInProject(ICompilerProject project) |
| { |
| if (!isImplicit()) |
| return super.isInProject(project); |
| // The implicit definitions are shared amoungst all projects. |
| if (this == NULL) |
| return true; |
| if (this == VOID) |
| return true; |
| if (this == ANY_TYPE) |
| return true; |
| if (this == UNDEFINED) |
| return true; |
| // Some implicit class definition we have never seen before. |
| return false; |
| } |
| |
| /** |
| * Method to find all the abstract methods declared in this class, and |
| * validate that the concrete class definition passed in implements those |
| * methods, and that they are implemented with compatible signatures |
| * |
| * @param cls the class definition to check |
| * @param problems a list of problems to report errors to |
| */ |
| public void validateClassImplementsAllMethods(ICompilerProject project, ClassDefinition cls, Collection<ICompilerProblem> problems) |
| { |
| ASScope classScope = cls.getContainedScope(); |
| |
| for (IDefinitionSet defSet : this.getContainedScope().getAllLocalDefinitionSets()) |
| { |
| for (int i = 0, l = defSet.getSize(); i < l; ++i) |
| { |
| IDefinition def = defSet.getDefinition(i); |
| if (def instanceof FunctionDefinition && !(def instanceof IAccessorDefinition)) |
| { |
| FunctionDefinition abstractMethod = (FunctionDefinition)def; |
| |
| // Skip any implicit methods added for CM compat |
| if (abstractMethod.isImplicit()) |
| continue; |
| |
| // Skip the constructor method of the interface. |
| if (abstractMethod.getBaseName().equals(getBaseName())) |
| continue; |
| |
| // Skip methods that are static |
| if (abstractMethod.isStatic()) |
| continue; |
| |
| // Skip methods that aren't abstract |
| if (!abstractMethod.isAbstract()) |
| continue; |
| |
| INamespaceDefinition ns = abstractMethod.resolveNamespace(project); |
| if(ns instanceof INamespaceDefinition.IProtectedNamespaceDefinition) |
| { |
| ns = cls.getProtectedNamespaceReference(); |
| } |
| |
| IDefinition c = classScope.getQualifiedPropertyFromDef(project, cls, abstractMethod.getBaseName(), ns, false); |
| if (c == null || c.isAbstract()) |
| { |
| // Error, didn't implement the method |
| problems.add(new UnimplementedAbstractMethodProblem(cls, |
| abstractMethod.getBaseName(), |
| this.getBaseName(), |
| cls.getBaseName())); |
| } |
| } |
| } |
| } |
| } |
| } |