/*
* 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.scope;
import com.google.dart.engine.AnalysisEngine;
import com.google.dart.engine.element.ClassElement;
import com.google.dart.engine.element.CompilationUnitElement;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.ExportElement;
import com.google.dart.engine.element.FunctionElement;
import com.google.dart.engine.element.FunctionTypeAliasElement;
import com.google.dart.engine.element.HideElementCombinator;
import com.google.dart.engine.element.ImportElement;
import com.google.dart.engine.element.LibraryElement;
import com.google.dart.engine.element.NamespaceCombinator;
import com.google.dart.engine.element.PrefixElement;
import com.google.dart.engine.element.PropertyAccessorElement;
import com.google.dart.engine.element.ShowElementCombinator;
import com.google.dart.engine.internal.context.InternalAnalysisContext;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
/**
* Instances of the class {@code NamespaceBuilder} are used to build a {@code Namespace}. Namespace
* builders are thread-safe and re-usable.
*
* @coverage dart.engine.resolver
*/
public class NamespaceBuilder {
/**
* Initialize a newly created namespace builder.
*/
public NamespaceBuilder() {
super();
}
/**
* Create a namespace representing the export namespace of the given {@link ExportElement}.
*
* @param element the export element whose export namespace is to be created
* @return the export namespace that was created
*/
public Namespace createExportNamespaceForDirective(ExportElement element) {
LibraryElement exportedLibrary = element.getExportedLibrary();
if (exportedLibrary == null) {
//
// The exported library will be null if the URI does not reference a valid library.
//
return Namespace.EMPTY;
}
HashMap<String, Element> definedNames = createExportMapping(
exportedLibrary,
new HashSet<LibraryElement>());
definedNames = applyCombinators(definedNames, element.getCombinators());
return new Namespace(definedNames);
}
/**
* Create a namespace representing the export namespace of the given library.
*
* @param library the library whose export namespace is to be created
* @return the export namespace that was created
*/
public Namespace createExportNamespaceForLibrary(LibraryElement library) {
return new Namespace(createExportMapping(library, new HashSet<LibraryElement>()));
}
/**
* Create a namespace representing the import namespace of the given library.
*
* @param library the library whose import namespace is to be created
* @return the import namespace that was created
*/
public Namespace createImportNamespaceForDirective(ImportElement element) {
LibraryElement importedLibrary = element.getImportedLibrary();
if (importedLibrary == null) {
//
// The imported library will be null if the URI does not reference a valid library.
//
return Namespace.EMPTY;
}
HashMap<String, Element> definedNames = createExportMapping(
importedLibrary,
new HashSet<LibraryElement>());
definedNames = applyCombinators(definedNames, element.getCombinators());
definedNames = applyPrefix(definedNames, element.getPrefix());
return new Namespace(definedNames);
}
/**
* Create a namespace representing the public namespace of the given library.
*
* @param library the library whose public namespace is to be created
* @return the public namespace that was created
*/
public Namespace createPublicNamespaceForLibrary(LibraryElement library) {
HashMap<String, Element> definedNames = new HashMap<String, Element>();
addPublicNames(definedNames, library.getDefiningCompilationUnit());
for (CompilationUnitElement compilationUnit : library.getParts()) {
addPublicNames(definedNames, compilationUnit);
}
return new Namespace(definedNames);
}
/**
* Add all of the names in the given namespace to the given mapping table.
*
* @param definedNames the mapping table to which the names in the given namespace are to be added
* @param namespace the namespace containing the names to be added to this namespace
*/
private void addAllFromMap(Map<String, Element> definedNames, Map<String, Element> newNames) {
for (Map.Entry<String, Element> entry : newNames.entrySet()) {
definedNames.put(entry.getKey(), entry.getValue());
}
}
/**
* Add all of the names in the given namespace to the given mapping table.
*
* @param definedNames the mapping table to which the names in the given namespace are to be added
* @param namespace the namespace containing the names to be added to this namespace
*/
private void addAllFromNamespace(Map<String, Element> definedNames, Namespace namespace) {
if (namespace != null) {
addAllFromMap(definedNames, namespace.getDefinedNames());
}
}
/**
* Add the given element to the given mapping table if it has a publicly visible name.
*
* @param definedNames the mapping table to which the public name is to be added
* @param element the element to be added
*/
private void addIfPublic(Map<String, Element> definedNames, Element element) {
String name = element.getName();
if (name != null && !Scope.isPrivateName(name)) {
definedNames.put(name, element);
}
}
/**
* Add to the given mapping table all of the public top-level names that are defined in the given
* compilation unit.
*
* @param definedNames the mapping table to which the public names are to be added
* @param compilationUnit the compilation unit defining the top-level names to be added to this
* namespace
*/
private void addPublicNames(Map<String, Element> definedNames,
CompilationUnitElement compilationUnit) {
for (PropertyAccessorElement element : compilationUnit.getAccessors()) {
addIfPublic(definedNames, element);
}
for (ClassElement element : compilationUnit.getEnums()) {
addIfPublic(definedNames, element);
}
for (FunctionElement element : compilationUnit.getFunctions()) {
addIfPublic(definedNames, element);
}
for (FunctionTypeAliasElement element : compilationUnit.getFunctionTypeAliases()) {
addIfPublic(definedNames, element);
}
for (ClassElement element : compilationUnit.getTypes()) {
addIfPublic(definedNames, element);
}
}
/**
* Apply the given combinators to all of the names in the given mapping table.
*
* @param definedNames the mapping table to which the namespace operations are to be applied
* @param combinators the combinators to be applied
*/
private HashMap<String, Element> applyCombinators(HashMap<String, Element> definedNames,
NamespaceCombinator[] combinators) {
for (NamespaceCombinator combinator : combinators) {
if (combinator instanceof HideElementCombinator) {
hide(definedNames, ((HideElementCombinator) combinator).getHiddenNames());
} else if (combinator instanceof ShowElementCombinator) {
definedNames = show(definedNames, ((ShowElementCombinator) combinator).getShownNames());
} else {
// Internal error.
AnalysisEngine.getInstance().getLogger().logError(
"Unknown type of combinator: " + combinator.getClass().getName());
}
}
return definedNames;
}
/**
* Apply the given prefix to all of the names in the table of defined names.
*
* @param definedNames the names that were defined before this operation
* @param prefixElement the element defining the prefix to be added to the names
*/
private HashMap<String, Element> applyPrefix(HashMap<String, Element> definedNames,
PrefixElement prefixElement) {
if (prefixElement != null) {
String prefix = prefixElement.getName();
HashMap<String, Element> newNames = new HashMap<String, Element>(definedNames.size());
for (Map.Entry<String, Element> entry : definedNames.entrySet()) {
newNames.put(prefix + "." + entry.getKey(), entry.getValue());
}
return newNames;
} else {
return definedNames;
}
}
/**
* Create a mapping table representing the export namespace of the given library.
*
* @param library the library whose public namespace is to be created
* @param visitedElements a set of libraries that do not need to be visited when processing the
* export directives of the given library because all of the names defined by them will
* be added by another library
* @return the mapping table that was created
*/
private HashMap<String, Element> createExportMapping(LibraryElement library,
HashSet<LibraryElement> visitedElements) {
visitedElements.add(library);
try {
HashMap<String, Element> definedNames = new HashMap<String, Element>();
for (ExportElement element : library.getExports()) {
LibraryElement exportedLibrary = element.getExportedLibrary();
if (exportedLibrary != null && !visitedElements.contains(exportedLibrary)) {
//
// The exported library will be null if the URI does not reference a valid library.
//
HashMap<String, Element> exportedNames = createExportMapping(
exportedLibrary,
visitedElements);
exportedNames = applyCombinators(exportedNames, element.getCombinators());
addAllFromMap(definedNames, exportedNames);
}
}
addAllFromNamespace(
definedNames,
((InternalAnalysisContext) library.getContext()).getPublicNamespace(library));
return definedNames;
} finally {
visitedElements.remove(library);
}
}
/**
* Hide all of the given names by removing them from the given collection of defined names.
*
* @param definedNames the names that were defined before this operation
* @param hiddenNames the names to be hidden
*/
private void hide(HashMap<String, Element> definedNames, String[] hiddenNames) {
for (String name : hiddenNames) {
definedNames.remove(name);
definedNames.remove(name + "=");
}
}
/**
* Show only the given names by removing all other names from the given collection of defined
* names.
*
* @param definedNames the names that were defined before this operation
* @param shownNames the names to be shown
*/
private HashMap<String, Element> show(HashMap<String, Element> definedNames, String[] shownNames) {
HashMap<String, Element> newNames = new HashMap<String, Element>(definedNames.size());
for (String name : shownNames) {
Element element = definedNames.get(name);
if (element != null) {
newNames.put(name, element);
}
String setterName = name + "=";
element = definedNames.get(setterName);
if (element != null) {
newNames.put(setterName, element);
}
}
return newNames;
}
}