/* * * 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.flex.compiler.internal.codegen.js.jx; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import org.apache.flex.compiler.asdoc.flexjs.ASDocComment; import org.apache.flex.compiler.codegen.ISubEmitter; import org.apache.flex.compiler.codegen.js.IJSEmitter; import org.apache.flex.compiler.constants.IASLanguageConstants; import org.apache.flex.compiler.definitions.*; import org.apache.flex.compiler.internal.codegen.as.ASEmitterTokens; import org.apache.flex.compiler.internal.codegen.js.JSSessionModel.ImplicitBindableImplementation; import org.apache.flex.compiler.internal.codegen.js.JSSubEmitter; import org.apache.flex.compiler.internal.codegen.js.flexjs.JSFlexJSEmitter; import org.apache.flex.compiler.internal.codegen.js.flexjs.JSFlexJSEmitterTokens; import org.apache.flex.compiler.internal.codegen.js.goog.JSGoogEmitterTokens; import org.apache.flex.compiler.internal.codegen.js.node.NodeEmitterTokens; import org.apache.flex.compiler.internal.codegen.js.utils.EmitterUtils; import org.apache.flex.compiler.internal.definitions.ClassDefinition; import org.apache.flex.compiler.internal.definitions.NamespaceDefinition.INamepaceDeclarationDirective; import org.apache.flex.compiler.internal.projects.FlexJSProject; import org.apache.flex.compiler.internal.scopes.ASProjectScope; import org.apache.flex.compiler.internal.scopes.PackageScope; import org.apache.flex.compiler.internal.tree.as.ClassNode; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.compiler.scopes.IASScope; import org.apache.flex.compiler.targets.ITarget.TargetType; import org.apache.flex.compiler.tree.as.ITypeNode; import org.apache.flex.compiler.units.ICompilationUnit; import org.apache.flex.compiler.utils.NativeUtils; public class PackageHeaderEmitter extends JSSubEmitter implements ISubEmitter<IPackageDefinition> { public PackageHeaderEmitter(IJSEmitter emitter) { super(emitter); } @Override public void emit(IPackageDefinition definition) { IASScope containedScope = definition.getContainedScope(); ITypeDefinition type = EmitterUtils.findType(containedScope .getAllLocalDefinitions()); String qname = null; boolean isExterns = false; if (type != null) { qname = type.getQualifiedName(); ITypeNode typeNode = type.getNode(); if (typeNode instanceof ClassNode) { ClassNode classNode = (ClassNode) typeNode; ASDocComment asDoc = (ASDocComment) classNode.getASDocComment(); if (asDoc != null) { String asDocString = asDoc.commentNoEnd(); isExterns = asDocString.contains(JSFlexJSEmitterTokens.EXTERNS.getToken()); getEmitter().getModel().isExterns = isExterns; } } } if (qname == null) { IFunctionDefinition fn = EmitterUtils.findFunction(containedScope .getAllLocalDefinitions()); if(fn != null) { qname = fn.getQualifiedName(); } } if (qname == null) { IVariableDefinition variable = EmitterUtils.findVariable(containedScope .getAllLocalDefinitions()); if(variable != null) { qname = variable.getQualifiedName(); } } if (qname == null) { INamepaceDeclarationDirective ns = EmitterUtils.findNamespace(containedScope .getAllLocalDefinitions()); if(ns != null) { qname = ns.getQualifiedName(); } } if (qname == null) { return; } FlexJSProject project = (FlexJSProject) getProject(); List<File> sourcePaths = project.getSourcePath(); String sourceName = definition.getSourcePath(); for (File sourcePath : sourcePaths) { if (sourceName.startsWith(sourcePath.getAbsolutePath())) { sourceName = sourceName.substring(sourcePath.getAbsolutePath().length() + 1); } } writeNewline("/**"); writeNewline(" * Generated by Apache Flex Cross-Compiler from " + sourceName); writeNewline(" * " + qname); writeNewline(" *"); writeNewline(" * @fileoverview"); if (isExterns) writeNewline(" * @externs"); writeNewline(" *"); // need to suppress access controls so access to protected/private from defineProperties // doesn't generate warnings. writeNewline(" * @suppress {checkTypes|accessControls}"); writeNewline(" */"); writeNewline(); /* goog.provide('x');\n\n */ write(JSGoogEmitterTokens.GOOG_PROVIDE); write(ASEmitterTokens.PAREN_OPEN); write(ASEmitterTokens.SINGLE_QUOTE); write(((JSFlexJSEmitter)getEmitter()).formatQualifiedName(qname, true)); write(ASEmitterTokens.SINGLE_QUOTE); write(ASEmitterTokens.PAREN_CLOSE); writeNewline(ASEmitterTokens.SEMICOLON); writeNewline(); } public void emitContents(IPackageDefinition definition) { // TODO (mschmalle) will remove this cast as more things get abstracted JSFlexJSEmitter fjs = (JSFlexJSEmitter) getEmitter(); PackageScope containedScope = (PackageScope) definition .getContainedScope(); ArrayList<String> writtenRequires = new ArrayList<String>(); Collection<IDefinition> localDefinitions = containedScope.getAllLocalDefinitions(); ITypeDefinition type = EmitterUtils.findType(localDefinitions); IDefinition otherMainDefinition = null; if (type == null) { if (localDefinitions.isEmpty()) return; // function or variable definition otherMainDefinition = localDefinitions.iterator().next(); } else { ITypeNode typeNode = type.getNode(); if (typeNode instanceof ClassNode) { ClassNode classNode = (ClassNode) typeNode; ASDocComment asDoc = (ASDocComment) classNode.getASDocComment(); if (asDoc != null) { String asDocString = asDoc.commentNoEnd(); String ignoreToken = JSFlexJSEmitterTokens.IGNORE_IMPORT .getToken(); int ignoreIndex = asDocString.indexOf(ignoreToken); while (ignoreIndex != -1) { String ignorable = asDocString.substring(ignoreIndex + ignoreToken.length()); int endIndex = ignorable.indexOf("\n"); ignorable = ignorable.substring(0, endIndex); ignorable = ignorable.trim(); // pretend we've already written the goog.requires for this writtenRequires.add(ignorable); ignoreIndex = asDocString.indexOf(ignoreToken, ignoreIndex + ignoreToken.length()); } } } } FlexJSProject flexProject = (FlexJSProject) getProject(); ASProjectScope projectScope = (ASProjectScope) flexProject.getScope(); ICompilationUnit cu = projectScope .getCompilationUnitForDefinition(type != null ? type : otherMainDefinition); ArrayList<String> requiresList = flexProject.getRequires(cu); ArrayList<String> interfacesList = flexProject.getInterfaces(cu); ArrayList<String> externalRequiresList = flexProject.getExternalRequires(cu); String cname = (type != null) ? type.getQualifiedName() : otherMainDefinition.getQualifiedName(); writtenRequires.add(cname); // make sure we don't add ourselves if (type instanceof IClassDefinition) { //check whether we should add the requires for the implicit Bindable EventDispatcher implementations boolean needsBindableSupport = ((IClassDefinition) type).needsEventDispatcher(flexProject); if (needsBindableSupport) { IClassDefinition bindableClassDef = (IClassDefinition) type; ClassDefinition objectClassDefinition = (ClassDefinition)flexProject.getBuiltinType( IASLanguageConstants.BuiltinType.OBJECT); if (bindableClassDef.resolveBaseClass(flexProject).equals(objectClassDefinition)) { //keep the decision in the model for later getModel().registerImplicitBindableImplementation( bindableClassDef, ImplicitBindableImplementation.EXTENDS); // add the requiresList support for extending the dispatcher class if (!requiresList.contains(fjs.formatQualifiedName(BindableEmitter.DISPATCHER_CLASS_QNAME))) { requiresList.add(fjs.formatQualifiedName(BindableEmitter.DISPATCHER_CLASS_QNAME)); } } else { //keep the decision in the model for later getModel().registerImplicitBindableImplementation( bindableClassDef, ImplicitBindableImplementation.IMPLEMENTS); //add the requiresList support for implementing IEventDispatcher if (!requiresList.contains(fjs.formatQualifiedName(BindableEmitter.DISPATCHER_INTERFACE_QNAME))) { requiresList.add(fjs.formatQualifiedName(BindableEmitter.DISPATCHER_INTERFACE_QNAME)); } if (!requiresList.contains(fjs.formatQualifiedName(BindableEmitter.DISPATCHER_CLASS_QNAME))) { requiresList.add(fjs.formatQualifiedName(BindableEmitter.DISPATCHER_CLASS_QNAME)); } } } if (!needsBindableSupport) { //we still need to check for static-only bindable requirements. If it was also instance-bindable, //then the static-only requirements have already been met above needsBindableSupport = ((IClassDefinition) type).needsStaticEventDispatcher(flexProject); //static-only bindable *only* requires the Dispatcher class, not the interface if (needsBindableSupport && !requiresList.contains(fjs.formatQualifiedName(BindableEmitter.DISPATCHER_CLASS_QNAME))) { requiresList.add(fjs.formatQualifiedName(BindableEmitter.DISPATCHER_CLASS_QNAME)); } } } boolean emitsRequires = emitRequires(requiresList, writtenRequires, cname); boolean emitsInterfaces = emitInterfaces(interfacesList, writtenRequires); // erikdebruin: Add missing language feature support, with e.g. 'is' and // 'as' operators. We don't need to worry about requiring // this in every project: ADVANCED_OPTIMISATIONS will NOT // include any of the code if it is not used in the project. boolean makingSWC = flexProject.getSWFTarget() != null && flexProject.getSWFTarget().getTargetType() == TargetType.SWC; boolean isMainCU = flexProject.mainCU != null && cu.getName().equals(flexProject.mainCU.getName()); if (isMainCU || makingSWC) { ICompilerProject project = this.getProject(); if (project instanceof FlexJSProject) { if (((FlexJSProject)project).needLanguage) { write(JSGoogEmitterTokens.GOOG_REQUIRE); write(ASEmitterTokens.PAREN_OPEN); write(ASEmitterTokens.SINGLE_QUOTE); write(JSFlexJSEmitterTokens.LANGUAGE_QNAME); write(ASEmitterTokens.SINGLE_QUOTE); write(ASEmitterTokens.PAREN_CLOSE); writeNewline(ASEmitterTokens.SEMICOLON); } } } boolean emitsExternalRequires = emitExternalRequires(externalRequiresList, writtenRequires); if (emitsRequires || emitsInterfaces || emitsExternalRequires || isMainCU) { writeNewline(); } writeNewline(); writeNewline(); } private boolean emitRequires(List<String> requiresList, List<String> writtenRequires, String cname) { boolean emitsRequires = false; if (requiresList != null) { Collections.sort(requiresList); for (String imp : requiresList) { if (imp.contains(JSGoogEmitterTokens.AS3.getToken())) continue; if (imp.equals(JSGoogEmitterTokens.GOOG_BIND.getToken())) continue; if (imp.equals(cname)) continue; if (NativeUtils.isNative(imp)) { if (!(imp.equals("QName") || imp.equals("Namespace") || imp.equals("XML") || imp.equals("XMLList"))) continue; } if (NativeUtils.isJSNative(imp)) { continue; } if (writtenRequires.indexOf(imp) == -1) { /* goog.require('x');\n */ write(JSGoogEmitterTokens.GOOG_REQUIRE); write(ASEmitterTokens.PAREN_OPEN); write(ASEmitterTokens.SINGLE_QUOTE); write(((JSFlexJSEmitter)getEmitter()).formatQualifiedName(imp, true)); write(ASEmitterTokens.SINGLE_QUOTE); write(ASEmitterTokens.PAREN_CLOSE); writeNewline(ASEmitterTokens.SEMICOLON); writtenRequires.add(imp); emitsRequires = true; } } } return emitsRequires; } private boolean emitInterfaces(List<String> interfacesList, List<String> writtenRequires) { boolean emitsInterfaces = false; if (interfacesList != null) { Collections.sort(interfacesList); for (String imp : interfacesList) { if (writtenRequires.indexOf(imp) == -1) { write(JSGoogEmitterTokens.GOOG_REQUIRE); write(ASEmitterTokens.PAREN_OPEN); write(ASEmitterTokens.SINGLE_QUOTE); write(((JSFlexJSEmitter)getEmitter()).formatQualifiedName(imp, true)); write(ASEmitterTokens.SINGLE_QUOTE); write(ASEmitterTokens.PAREN_CLOSE); writeNewline(ASEmitterTokens.SEMICOLON); emitsInterfaces = true; } } } return emitsInterfaces; } private boolean emitExternalRequires(List<String> externalRequiresList, List<String> writtenRequires) { boolean emitsExternalRequires = false; if (externalRequiresList != null) { Collections.sort(externalRequiresList); for (String imp : externalRequiresList) { if (writtenRequires.indexOf(imp) == -1) { /* var x = require('x');\n */ write(ASEmitterTokens.VAR); write(ASEmitterTokens.SPACE); write(imp); write(ASEmitterTokens.SPACE); write(ASEmitterTokens.EQUAL); write(ASEmitterTokens.SPACE); write(NodeEmitterTokens.REQUIRE); write(ASEmitterTokens.PAREN_OPEN); write(ASEmitterTokens.SINGLE_QUOTE); write(imp); write(ASEmitterTokens.SINGLE_QUOTE); write(ASEmitterTokens.PAREN_CLOSE); writeNewline(ASEmitterTokens.SEMICOLON); writtenRequires.add(imp); emitsExternalRequires = true; } } } return emitsExternalRequires; } }