/* * Copyright (c) 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html * * 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 com.google.dart.engine.internal.element; import com.google.common.collect.Sets; import com.google.dart.engine.AnalysisEngine; import com.google.dart.engine.ast.LibraryIdentifier; import com.google.dart.engine.context.AnalysisContext; import com.google.dart.engine.context.AnalysisException; import com.google.dart.engine.element.ClassElement; import com.google.dart.engine.element.CompilationUnitElement; import com.google.dart.engine.element.ElementKind; import com.google.dart.engine.element.ElementVisitor; import com.google.dart.engine.element.ExportElement; import com.google.dart.engine.element.FunctionElement; import com.google.dart.engine.element.ImportElement; import com.google.dart.engine.element.LibraryElement; import com.google.dart.engine.element.PrefixElement; import com.google.dart.engine.internal.type.DynamicTypeImpl; import com.google.dart.engine.internal.type.FunctionTypeImpl; import com.google.dart.engine.internal.type.VoidTypeImpl; import com.google.dart.engine.sdk.DartSdk; import com.google.dart.engine.source.Source; import com.google.dart.engine.type.InterfaceType; import com.google.dart.engine.type.Type; import com.google.dart.engine.utilities.general.StringUtilities; import com.google.dart.engine.utilities.translation.DartName; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Instances of the class {@code LibraryElementImpl} implement a {@code LibraryElement}. * * @coverage dart.engine.element */ public class LibraryElementImpl extends ElementImpl implements LibraryElement { /** * An empty array of library elements. */ public static final LibraryElement[] EMPTY_ARRAY = new LibraryElement[0]; /** * Determine if the given library is up to date with respect to the given time stamp. * * @param library the library to process * @param timeStamp the time stamp to check against * @param visitedLibraries the set of visited libraries */ private static boolean safeIsUpToDate(LibraryElement library, long timeStamp, Set<LibraryElement> visitedLibraries) { if (!visitedLibraries.contains(library)) { visitedLibraries.add(library); AnalysisContext context = library.getContext(); // Check the defining compilation unit. if (timeStamp < context.getModificationStamp(library.getDefiningCompilationUnit().getSource())) { return false; } // Check the parted compilation units. for (CompilationUnitElement element : library.getParts()) { if (timeStamp < context.getModificationStamp(element.getSource())) { return false; } } // Check the imported libraries. for (LibraryElement importedLibrary : library.getImportedLibraries()) { if (!safeIsUpToDate(importedLibrary, timeStamp, visitedLibraries)) { return false; } } // Check the exported libraries. for (LibraryElement exportedLibrary : library.getExportedLibraries()) { if (!safeIsUpToDate(exportedLibrary, timeStamp, visitedLibraries)) { return false; } } } return true; } /** * The analysis context in which this library is defined. */ private AnalysisContext context; /** * The compilation unit that defines this library. */ private CompilationUnitElement definingCompilationUnit; /** * The entry point for this library, or {@code null} if this library does not have an entry point. */ private FunctionElement entryPoint; /** * An array containing specifications of all of the imports defined in this library. */ private ImportElement[] imports = ImportElement.EMPTY_ARRAY; /** * An array containing specifications of all of the exports defined in this library. */ private ExportElement[] exports = ExportElement.EMPTY_ARRAY; /** * An array containing all of the compilation units that are included in this library using a * {@code part} directive. */ private CompilationUnitElement[] parts = CompilationUnitElementImpl.EMPTY_ARRAY; /** * Is {@code true} if this library is created for Angular analysis. */ private boolean isAngularHtml; /** * The element representing the synthetic function {@code loadLibrary} that is defined for this * library, or {@code null} if the element has not yet been created. */ private FunctionElement loadLibraryFunction; /** * Initialize a newly created library element to have the given name. * * @param context the analysis context in which the library is defined * @param name the name of this element */ @DartName("forNode") public LibraryElementImpl(AnalysisContext context, LibraryIdentifier name) { super(name); this.context = context; } /** * Initialize a newly created library element to have the given name. * * @param context the analysis context in which the library is defined * @param name the name of this element * @param nameOffset the offset of the name of this element in the file that contains the * declaration of this element */ public LibraryElementImpl(AnalysisContext context, String name, int nameOffset) { super(name, nameOffset); this.context = context; } @Override public <R> R accept(ElementVisitor<R> visitor) { return visitor.visitLibraryElement(this); } @Override public boolean equals(Object object) { return object != null && getClass() == object.getClass() && definingCompilationUnit.equals(((LibraryElementImpl) object).getDefiningCompilationUnit()); } @Override public ElementImpl getChild(String identifier) { if (((CompilationUnitElementImpl) definingCompilationUnit).getIdentifier().equals(identifier)) { return (CompilationUnitElementImpl) definingCompilationUnit; } for (CompilationUnitElement part : parts) { if (((CompilationUnitElementImpl) part).getIdentifier().equals(identifier)) { return (CompilationUnitElementImpl) part; } } for (ImportElement importElement : imports) { if (((ImportElementImpl) importElement).getIdentifier().equals(identifier)) { return (ImportElementImpl) importElement; } } for (ExportElement exportElement : exports) { if (((ExportElementImpl) exportElement).getIdentifier().equals(identifier)) { return (ExportElementImpl) exportElement; } } return null; } @Override public AnalysisContext getContext() { return context; } @Override public CompilationUnitElement getDefiningCompilationUnit() { return definingCompilationUnit; } @Override public FunctionElement getEntryPoint() { return entryPoint; } @Override public LibraryElement[] getExportedLibraries() { HashSet<LibraryElement> libraries = new HashSet<LibraryElement>(exports.length); for (ExportElement element : exports) { LibraryElement library = element.getExportedLibrary(); if (library != null) { libraries.add(library); } } return libraries.toArray(new LibraryElement[libraries.size()]); } @Override public ExportElement[] getExports() { return exports; } @Override public LibraryElement[] getImportedLibraries() { HashSet<LibraryElement> libraries = new HashSet<LibraryElement>(imports.length); for (ImportElement element : imports) { LibraryElement library = element.getImportedLibrary(); if (library != null) { libraries.add(library); } } return libraries.toArray(new LibraryElement[libraries.size()]); } @Override public ImportElement[] getImports() { return imports; } @Override public ImportElement[] getImportsWithPrefix(PrefixElement prefixElement) { int count = imports.length; ArrayList<ImportElement> importList = new ArrayList<ImportElement>(count); for (int i = 0; i < count; i++) { if (imports[i].getPrefix() == prefixElement) { importList.add(imports[i]); } } return importList.toArray(new ImportElement[importList.size()]); } @Override public ElementKind getKind() { return ElementKind.LIBRARY; } @Override public LibraryElement getLibrary() { return this; } @Override public FunctionElement getLoadLibraryFunction() { if (loadLibraryFunction == null) { FunctionElementImpl function = new FunctionElementImpl(FunctionElement.LOAD_LIBRARY_NAME, -1); function.setSynthetic(true); function.setEnclosingElement(this); function.setReturnType(getLoadLibraryReturnType()); function.setType(new FunctionTypeImpl(function)); loadLibraryFunction = function; } return loadLibraryFunction; } @Override public CompilationUnitElement[] getParts() { return parts; } @Override public PrefixElement[] getPrefixes() { HashSet<PrefixElement> prefixes = new HashSet<PrefixElement>(imports.length); for (ImportElement element : imports) { PrefixElement prefix = element.getPrefix(); if (prefix != null) { prefixes.add(prefix); } } return prefixes.toArray(new PrefixElement[prefixes.size()]); } @Override public Source getSource() { if (definingCompilationUnit == null) { return null; } return definingCompilationUnit.getSource(); } @Override public ClassElement getType(String className) { ClassElement type = definingCompilationUnit.getType(className); if (type != null) { return type; } for (CompilationUnitElement part : parts) { type = part.getType(className); if (type != null) { return type; } } return null; } @Override public CompilationUnitElement[] getUnits() { CompilationUnitElement[] units = new CompilationUnitElement[1 + parts.length]; units[0] = definingCompilationUnit; System.arraycopy(parts, 0, units, 1, parts.length); return units; } @Override public LibraryElement[] getVisibleLibraries() { Set<LibraryElement> visibleLibraries = Sets.newHashSet(); addVisibleLibraries(visibleLibraries, false); return visibleLibraries.toArray(new LibraryElement[visibleLibraries.size()]); } @Override public boolean hasExtUri() { return hasModifier(Modifier.HAS_EXT_URI); } @Override public int hashCode() { return definingCompilationUnit.hashCode(); } @Override public boolean hasLoadLibraryFunction() { if (definingCompilationUnit.hasLoadLibraryFunction()) { return true; } for (int i = 0; i < parts.length; i++) { if (parts[i].hasLoadLibraryFunction()) { return true; } } return false; } @Override public boolean isAngularHtml() { return isAngularHtml; } @Override public boolean isBrowserApplication() { return entryPoint != null && isOrImportsBrowserLibrary(); } @Override public boolean isDartCore() { return getName().equals("dart.core"); } @Override public boolean isInSdk() { return StringUtilities.startsWith5(getName(), 0, 'd', 'a', 'r', 't', '.'); } @Override public boolean isUpToDate(long timeStamp) { Set<LibraryElement> visitedLibraries = Sets.newHashSet(); return safeIsUpToDate(this, timeStamp, visitedLibraries); } /** * Specifies if this library is created for Angular analysis. */ public void setAngularHtml(boolean isAngularHtml) { this.isAngularHtml = isAngularHtml; } /** * Set the compilation unit that defines this library to the given compilation unit. * * @param definingCompilationUnit the compilation unit that defines this library */ public void setDefiningCompilationUnit(CompilationUnitElement definingCompilationUnit) { ((CompilationUnitElementImpl) definingCompilationUnit).setEnclosingElement(this); this.definingCompilationUnit = definingCompilationUnit; } /** * Set the entry point for this library to the given function. * * @param entryPoint the entry point for this library */ public void setEntryPoint(FunctionElement entryPoint) { this.entryPoint = entryPoint; } /** * Set the specifications of all of the exports defined in this library to the given array. * * @param exports the specifications of all of the exports defined in this library */ public void setExports(ExportElement[] exports) { for (ExportElement exportElement : exports) { ((ExportElementImpl) exportElement).setEnclosingElement(this); } this.exports = exports; } /** * Set whether this library has an import of a "dart-ext" URI to the given value. * * @param hasExtUri {@code true} if this library has an import of a "dart-ext" URI */ public void setHasExtUri(boolean hasExtUri) { setModifier(Modifier.HAS_EXT_URI, hasExtUri); } /** * Set the specifications of all of the imports defined in this library to the given array. * * @param imports the specifications of all of the imports defined in this library */ public void setImports(ImportElement[] imports) { for (ImportElement importElement : imports) { ((ImportElementImpl) importElement).setEnclosingElement(this); PrefixElementImpl prefix = (PrefixElementImpl) importElement.getPrefix(); if (prefix != null) { prefix.setEnclosingElement(this); } } this.imports = imports; } /** * Set the compilation units that are included in this library using a {@code part} directive. * * @param parts the compilation units that are included in this library using a {@code part} * directive */ public void setParts(CompilationUnitElement[] parts) { for (CompilationUnitElement compilationUnit : parts) { ((CompilationUnitElementImpl) compilationUnit).setEnclosingElement(this); } this.parts = parts; } @Override public void visitChildren(ElementVisitor<?> visitor) { super.visitChildren(visitor); safelyVisitChild(definingCompilationUnit, visitor); safelyVisitChildren(exports, visitor); safelyVisitChildren(imports, visitor); safelyVisitChildren(parts, visitor); } @Override protected String getIdentifier() { return definingCompilationUnit.getSource().getEncoding(); } /** * Recursively fills set of visible libraries for {@link #getVisibleElementsLibraries}. */ private void addVisibleLibraries(Set<LibraryElement> visibleLibraries, boolean includeExports) { // maybe already processed if (!visibleLibraries.add(this)) { return; } // add imported libraries for (ImportElement importElement : imports) { LibraryElement importedLibrary = importElement.getImportedLibrary(); if (importedLibrary != null) { ((LibraryElementImpl) importedLibrary).addVisibleLibraries(visibleLibraries, true); } } // add exported libraries if (includeExports) { for (ExportElement exportElement : exports) { LibraryElement exportedLibrary = exportElement.getExportedLibrary(); if (exportedLibrary != null) { ((LibraryElementImpl) exportedLibrary).addVisibleLibraries(visibleLibraries, true); } } } } /** * Return the object representing the type "Future" from the dart:async library, or the type * "void" if the type "Future" cannot be accessed. * * @return the type "Future" from the dart:async library */ private Type getLoadLibraryReturnType() { try { Source asyncSource = context.getSourceFactory().forUri(DartSdk.DART_ASYNC); if (asyncSource == null) { AnalysisEngine.getInstance().getLogger().logError( "Could not create a source for dart:async"); return VoidTypeImpl.getInstance(); } LibraryElement asyncElement = context.computeLibraryElement(asyncSource); if (asyncElement == null) { AnalysisEngine.getInstance().getLogger().logError( "Could not build the element model for dart:async"); return VoidTypeImpl.getInstance(); } ClassElement futureElement = asyncElement.getType("Future"); if (futureElement == null) { AnalysisEngine.getInstance().getLogger().logError( "Could not find type Future in dart:async"); return VoidTypeImpl.getInstance(); } InterfaceType futureType = futureElement.getType(); return futureType.substitute(new Type[] {DynamicTypeImpl.getInstance()}); } catch (AnalysisException exception) { AnalysisEngine.getInstance().getLogger().logError( "Could not build the element model for dart:async", exception); return VoidTypeImpl.getInstance(); } } /** * Answer {@code true} if the receiver directly or indirectly imports the dart:html libraries. * * @return {@code true} if the receiver directly or indirectly imports the dart:html libraries */ private boolean isOrImportsBrowserLibrary() { List<LibraryElement> visited = new ArrayList<LibraryElement>(10); Source htmlLibSource = context.getSourceFactory().forUri(DartSdk.DART_HTML); visited.add(this); for (int index = 0; index < visited.size(); index++) { LibraryElement library = visited.get(index); Source source = library.getDefiningCompilationUnit().getSource(); if (source.equals(htmlLibSource)) { return true; } for (LibraryElement importedLibrary : library.getImportedLibraries()) { if (!visited.contains(importedLibrary)) { visited.add(importedLibrary); } } for (LibraryElement exportedLibrary : library.getExportedLibraries()) { if (!visited.contains(exportedLibrary)) { visited.add(exportedLibrary); } } } return false; } }