/*
* Copyright 2008 CoreMedia AG
*
* Licensed 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 net.jangaroo.jooc.ast;
import net.jangaroo.jooc.JangarooParser;
import net.jangaroo.jooc.JooSymbol;
import net.jangaroo.jooc.Jooc;
import net.jangaroo.jooc.Scope;
import net.jangaroo.jooc.input.InputSource;
import net.jangaroo.utils.AS3Type;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Andreas Gawecki
* @author Frank Wienberg
*/
public class CompilationUnit extends NodeImplBase {
private PackageDeclaration packageDeclaration;
private JooSymbol lBrace;
private List<AstNode> directives;
private IdeDeclaration primaryDeclaration;
private JooSymbol rBrace;
private boolean hasStaticCode = false;
private Set<String> dependencies = new LinkedHashSet<String>();
private Set<CompilationUnit> dependenciesAsCompilationUnits = new LinkedHashSet<CompilationUnit>();
private Set<String> publicApiDependencies = new HashSet<String>();
private Set<String> usedBuiltIns = new LinkedHashSet<String>();
private Scope scope;
private Map<String, String> auxVarsByPackage = new LinkedHashMap<String, String>();
private boolean auxVarsRendered;
private InputSource source;
private JangarooParser compiler;
public CompilationUnit(PackageDeclaration packageDeclaration, JooSymbol lBrace, List<AstNode> directives, IdeDeclaration primaryDeclaration, JooSymbol rBrace, List<IdeDeclaration> secondaryDeclarations) {
this.packageDeclaration = packageDeclaration;
this.lBrace = lBrace;
this.directives = directives;
this.primaryDeclaration = primaryDeclaration;
if (primaryDeclaration instanceof ClassDeclaration) {
((ClassDeclaration) primaryDeclaration).setSecondaryDeclarations(secondaryDeclarations);
}
this.rBrace = rBrace;
}
@Override
public List<? extends AstNode> getChildren() {
List<AstNode> result = new ArrayList<AstNode>(makeChildren(super.getChildren(), packageDeclaration, directives, primaryDeclaration));
if (primaryDeclaration instanceof ClassDeclaration) {
result.addAll(((ClassDeclaration) primaryDeclaration).getSecondaryDeclarations());
}
return result;
}
public List<AstNode> getDirectives() {
return directives;
}
public void addBuiltInUsage(String builtIn) {
usedBuiltIns.add(builtIn);
}
public String getAuxVarForPackage(String packageQName) {
return auxVarsByPackage.get(packageQName);
}
public String getAuxVarForPackage(Scope lookupScope, String packageQName) {
if (auxVarsRendered) {
throw new IllegalStateException("aux vars already rendered!");
}
String auxVar = getAuxVarForPackage(packageQName);
if (auxVar == null) {
auxVar = scope.createAuxVar(lookupScope).getName();
auxVarsByPackage.put(packageQName, auxVar);
}
return auxVar;
}
public Map<String, String> getAuxVarDeclarations() {
auxVarsRendered = true;
LinkedHashMap<String, String> result = new LinkedHashMap<String, String>();
for (String builtIn : usedBuiltIns) {
String value = "joo." + ("$$bound".equals(builtIn) ? "boundMethod" : builtIn);
result.put(builtIn, value);
}
for (Map.Entry<String,String> entry : auxVarsByPackage.entrySet()) {
result.put(entry.getValue(), entry.getKey());
}
return result;
}
@Override
public void visit(AstVisitor visitor) throws IOException {
visitor.visitCompilationUnit(this);
}
private void addStarImport(final Ide packageIde) {
ImportDirective importDirective = new ImportDirective(packageIde, AS3Type.ANY.toString());
directives.add(0, importDirective);
}
@Override
public void scope(final Scope scope) {
withNewDeclarationScope(this, scope, new Scoped() {
@Override
public void run(final Scope scope) {
Ide packageIde = packageDeclaration.getIde();
packageDeclaration.scope(scope);
if (packageIde != null) {
// add implicit same package import
addStarImport(packageIde);
}
// add implicit toplevel package import
addStarImport(null);
for (AstNode node : getDirectives()) {
node.scope(scope);
}
withNewDeclarationScope(packageDeclaration, scope, new Scoped() {
@Override
public void run(final Scope scope) {
CompilationUnit.this.scope = scope;
primaryDeclaration.scope(scope);
}
});
}
});
}
public PackageDeclaration getPackageDeclaration() {
return packageDeclaration;
}
public IdeDeclaration getPrimaryDeclaration() {
return primaryDeclaration;
}
public JooSymbol getLBrace() {
return lBrace;
}
public JooSymbol getRBrace() {
return rBrace;
}
public Set<String> getDependencies() {
return dependencies;
}
public Set<String> getPublicApiDependencies() {
return publicApiDependencies;
}
public Set<CompilationUnit> getDependenciesAsCompilationUnits() {
return dependenciesAsCompilationUnits;
}
public JangarooParser getCompiler() {
return compiler;
}
public void setCompiler(final JangarooParser compiler) {
this.compiler = compiler;
}
/**
* @param source the source of this compilation unit.
*/
public void setSource(InputSource source) {
this.source = source;
}
public InputSource getSource() {
return source;
}
public void analyze(AstNode parentNode) {
super.analyze(parentNode);
packageDeclaration.analyze(this);
primaryDeclaration.analyze(this);
}
public Annotation getAnnotation(String name) {
List<AstNode> directives = getDirectives();
for (AstNode directive : directives) {
if (directive instanceof Annotation) {
Annotation annotation = (Annotation)directive;
if (name.equals(annotation.getMetaName())) {
return annotation;
}
}
}
return null;
}
public JooSymbol getSymbol() {
return packageDeclaration.getSymbol();
}
public void setHasStaticCode() {
hasStaticCode = true;
}
public boolean isHasStaticCode() {
return hasStaticCode;
}
public void addDependency(CompilationUnit otherUnit) {
// predefined ides have a null unit
if (otherUnit != null && otherUnit != this && otherUnit.getAnnotation(Jooc.NATIVE_ANNOTATION_NAME) == null) {
String qname = otherUnit.getPrimaryDeclaration().getQualifiedNameStr();
dependencies.add(qname);
dependenciesAsCompilationUnits.add(otherUnit);
}
}
public void addPublicApiDependency(CompilationUnit otherUnit) {
// predefined ides have a null unit
if (otherUnit != null && otherUnit != this) {
//todo extend runtime to load units with primary decls other than classes
final IdeDeclaration otherUnitPrimaryDeclaration = otherUnit.getPrimaryDeclaration();
if (otherUnitPrimaryDeclaration instanceof ClassDeclaration
|| otherUnitPrimaryDeclaration instanceof NamespaceDeclaration) {
// It is a class ...
String qname = otherUnitPrimaryDeclaration.getQualifiedNameStr();
if (qname.indexOf('.') != -1) {
// ... outside of the root package.
publicApiDependencies.add(qname);
}
}
}
}
/**
* Add a dependency to a resource at the given path, which is relative to this compilation unit's file.
*
* @param relativePath relative path of the dependency
* @return the path relative to the source directory
*/
public String addResourceDependency(String relativePath) {
String path = relativePath.startsWith("/") || relativePath.startsWith("\\")
? relativePath
: new File(source.getParent().getRelativePath(), relativePath).getPath().replace('\\', '/');
if (path.startsWith("/")) {
path = path.substring(1);
}
dependencies.add("resource:" + path);
return path;
}
@Override
public String toString() {
return getClass().getSimpleName() + "(" + getPrimaryDeclaration().getQualifiedNameStr() + ")";
}
}