Package net.jangaroo.jooc.backend

Source Code of net.jangaroo.jooc.backend.ApiModelGenerator

package net.jangaroo.jooc.backend;

import net.jangaroo.jooc.JooSymbol;
import net.jangaroo.jooc.Jooc;
import net.jangaroo.jooc.ast.Annotation;
import net.jangaroo.jooc.ast.AnnotationParameter;
import net.jangaroo.jooc.ast.ApplyExpr;
import net.jangaroo.jooc.ast.ArrayIndexExpr;
import net.jangaroo.jooc.ast.ArrayLiteral;
import net.jangaroo.jooc.ast.AsExpr;
import net.jangaroo.jooc.ast.AssignmentOpExpr;
import net.jangaroo.jooc.ast.AstNode;
import net.jangaroo.jooc.ast.AstVisitor;
import net.jangaroo.jooc.ast.BinaryOpExpr;
import net.jangaroo.jooc.ast.BlockStatement;
import net.jangaroo.jooc.ast.BreakStatement;
import net.jangaroo.jooc.ast.CaseStatement;
import net.jangaroo.jooc.ast.Catch;
import net.jangaroo.jooc.ast.ClassBody;
import net.jangaroo.jooc.ast.ClassDeclaration;
import net.jangaroo.jooc.ast.CommaSeparatedList;
import net.jangaroo.jooc.ast.CompilationUnit;
import net.jangaroo.jooc.ast.ConditionalExpr;
import net.jangaroo.jooc.ast.ContinueStatement;
import net.jangaroo.jooc.ast.Declaration;
import net.jangaroo.jooc.ast.DefaultStatement;
import net.jangaroo.jooc.ast.Directive;
import net.jangaroo.jooc.ast.DoStatement;
import net.jangaroo.jooc.ast.DotExpr;
import net.jangaroo.jooc.ast.EmptyDeclaration;
import net.jangaroo.jooc.ast.EmptyStatement;
import net.jangaroo.jooc.ast.Expr;
import net.jangaroo.jooc.ast.Extends;
import net.jangaroo.jooc.ast.ForInStatement;
import net.jangaroo.jooc.ast.ForInitializer;
import net.jangaroo.jooc.ast.ForStatement;
import net.jangaroo.jooc.ast.FunctionDeclaration;
import net.jangaroo.jooc.ast.FunctionExpr;
import net.jangaroo.jooc.ast.Ide;
import net.jangaroo.jooc.ast.IdeExpr;
import net.jangaroo.jooc.ast.IdeWithTypeParam;
import net.jangaroo.jooc.ast.IfStatement;
import net.jangaroo.jooc.ast.Implements;
import net.jangaroo.jooc.ast.ImportDirective;
import net.jangaroo.jooc.ast.InfixOpExpr;
import net.jangaroo.jooc.ast.Initializer;
import net.jangaroo.jooc.ast.IsExpr;
import net.jangaroo.jooc.ast.LabeledStatement;
import net.jangaroo.jooc.ast.LiteralExpr;
import net.jangaroo.jooc.ast.NamespaceDeclaration;
import net.jangaroo.jooc.ast.NamespacedIde;
import net.jangaroo.jooc.ast.NewExpr;
import net.jangaroo.jooc.ast.ObjectField;
import net.jangaroo.jooc.ast.ObjectLiteral;
import net.jangaroo.jooc.ast.PackageDeclaration;
import net.jangaroo.jooc.ast.Parameter;
import net.jangaroo.jooc.ast.Parameters;
import net.jangaroo.jooc.ast.ParenthesizedExpr;
import net.jangaroo.jooc.ast.PostfixOpExpr;
import net.jangaroo.jooc.ast.PredefinedTypeDeclaration;
import net.jangaroo.jooc.ast.PrefixOpExpr;
import net.jangaroo.jooc.ast.QualifiedIde;
import net.jangaroo.jooc.ast.ReturnStatement;
import net.jangaroo.jooc.ast.SemicolonTerminatedStatement;
import net.jangaroo.jooc.ast.SuperConstructorCallStatement;
import net.jangaroo.jooc.ast.SwitchStatement;
import net.jangaroo.jooc.ast.ThrowStatement;
import net.jangaroo.jooc.ast.TryStatement;
import net.jangaroo.jooc.ast.Type;
import net.jangaroo.jooc.ast.TypeRelation;
import net.jangaroo.jooc.ast.TypedIdeDeclaration;
import net.jangaroo.jooc.ast.UseNamespaceDirective;
import net.jangaroo.jooc.ast.VariableDeclaration;
import net.jangaroo.jooc.ast.VectorLiteral;
import net.jangaroo.jooc.ast.WhileStatement;
import net.jangaroo.jooc.model.ActionScriptModel;
import net.jangaroo.jooc.model.AnnotatedModel;
import net.jangaroo.jooc.model.AnnotationModel;
import net.jangaroo.jooc.model.AnnotationPropertyModel;
import net.jangaroo.jooc.model.ClassModel;
import net.jangaroo.jooc.model.CompilationUnitModel;
import net.jangaroo.jooc.model.DocumentedModel;
import net.jangaroo.jooc.model.FieldModel;
import net.jangaroo.jooc.model.MemberModel;
import net.jangaroo.jooc.model.MethodModel;
import net.jangaroo.jooc.model.MethodType;
import net.jangaroo.jooc.model.NamespacedModel;
import net.jangaroo.jooc.model.NamedModel;
import net.jangaroo.jooc.model.NamespaceModel;
import net.jangaroo.jooc.model.ParamModel;
import net.jangaroo.jooc.model.TypedModel;
import net.jangaroo.jooc.model.ValuedModel;

import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.TreeSet;

/**
* Generates a model of the given compilation unit AST.
* Feeding this model to an {@link ActionScriptCodeGeneratingModelVisitor} generates reduced ActionScript source code.
* The reduced code is used as reference during compilation
* and can be run through the asdoc tool.
*/
public class ApiModelGenerator {

  private boolean excludeClassByDefault = false;

  public ApiModelGenerator(boolean excludeClassByDefault) {
    this.excludeClassByDefault = excludeClassByDefault;
  }

  public boolean isExcludeClassByDefault() {
    return excludeClassByDefault;
  }

  public CompilationUnitModel generateModel(CompilationUnit compilationUnit) throws IOException {
    ApiModelGeneratingAstVisitor visitor = new ApiModelGeneratingAstVisitor();
    compilationUnit.visit(visitor);
    return visitor.getCurrent(CompilationUnitModel.class);
  }

 
  private class ApiModelGeneratingAstVisitor implements AstVisitor {

  private Deque<ActionScriptModel> modelStack;
  private StringBuilder code;
  private StringBuilder asdoc = new StringBuilder();
  private List<AnnotationModel> annotationModels = new ArrayList<AnnotationModel>();

  @Override
  public void visitTypeRelation(TypeRelation typeRelation) throws IOException {
    startRecordingCode();
    typeRelation.getType().visit(this);
    ((TypedModel)modelStack.peek()).setType(consumeRecordedCode());
  }

  @Override
  public void visitAnnotationParameter(AnnotationParameter annotationParameter) throws IOException {
    AnnotationPropertyModel annotationPropertyModel = new AnnotationPropertyModel();
    modelStack.push(annotationPropertyModel);
    visitIfNotNull(annotationParameter.getOptName());
    startRecordingCode();
    visitIfNotNull(annotationParameter.getValue());
    annotationPropertyModel.setValue(consumeRecordedCode());
    modelStack.pop();
    ((AnnotationModel)modelStack.peek()).addProperty(annotationPropertyModel);
  }

  @Override
  public void visitExtends(Extends anExtends) throws IOException {
    getCurrent(ClassModel.class).setSuperclass(anExtends.getSuperClass().getQualifiedNameStr());
  }

  @Override
  public void visitInitializer(Initializer initializer) throws IOException {
    if (initializer.getValue().isCompileTimeConstant()) {
      startRecordingCode();
      initializer.getValue().visit(this);
      ((ValuedModel)modelStack.peek()).setValue(consumeRecordedCode());
    }
  }

  @Override
  public void visitObjectField(ObjectField objectField) throws IOException {
    throw new UnsupportedOperationException();
  }

  @Override
  public void visitForInitializer(ForInitializer forInitializer) throws IOException {
    throw new UnsupportedOperationException();
  }

  @Override
  public void visitCompilationUnit(CompilationUnit compilationUnit) throws IOException {
    modelStack = new ArrayDeque<ActionScriptModel>();
    code = null;
    CompilationUnitModel compilationUnitModel = new CompilationUnitModel("");
    modelStack.push(compilationUnitModel);
    compilationUnit.getPackageDeclaration().visit(this);
    for (String publicApiDependency : new TreeSet<String>(compilationUnit.getPublicApiDependencies())) {
      compilationUnitModel.addImport(publicApiDependency);
    }
    visitAll(compilationUnit.getDirectives());
    compilationUnit.getPrimaryDeclaration().visit(this);
  }

  @Override
  public void visitIde(Ide ide) throws IOException {
    if (code != null) {
      recordCode(ide.getQualifiedNameStr());
    } else {
      ((NamedModel)modelStack.peek()).setName(ide.getQualifiedNameStr());
    }
  }

  @Override
  public void visitQualifiedIde(QualifiedIde qualifiedIde) throws IOException {
    visitIde(qualifiedIde);
  }

  @Override
  public void visitIdeWithTypeParam(IdeWithTypeParam ideWithTypeParam) throws IOException {
    recordCode(
      ideWithTypeParam.getOriginalIde().getText()
        + ideWithTypeParam.getSymDotLt().getText());
    ideWithTypeParam.getType().visit(this);
    recordCode(ideWithTypeParam.getSymGt().getText());
  }

  @Override
  public void visitNamespacedIde(NamespacedIde namespacedIde) throws IOException {
    throw new UnsupportedOperationException();
  }

  @Override
  public void visitImplements(Implements anImplements) throws IOException {
    CommaSeparatedList<Ide> superTypes = anImplements.getSuperTypes();
    while (superTypes != null) {
      getCurrent(ClassModel.class).addInterface(superTypes.getHead().getQualifiedNameStr());
      superTypes = superTypes.getTail();
    }
  }

  @Override
  public void visitType(Type type) throws IOException {
    type.getIde().visit(this);
  }

  @Override
  public void visitObjectLiteral(ObjectLiteral objectLiteral) throws IOException {
    throw shouldOnlyBeCalledForCompileTimeConstants();
  }

  private IllegalStateException shouldOnlyBeCalledForCompileTimeConstants() {
    return new IllegalStateException("should only be called for compile time constants");
  }

  @Override
  public void visitIdeExpression(IdeExpr ideExpr) throws IOException {
    ideExpr.getIde().visit(this);
  }

  @Override
  public <T extends Expr> void visitParenthesizedExpr(ParenthesizedExpr<T> parenthesizedExpr) throws IOException {
    throw shouldOnlyBeCalledForCompileTimeConstants();
  }

  @Override
  public void visitArrayLiteral(ArrayLiteral arrayLiteral) throws IOException {
    throw shouldOnlyBeCalledForCompileTimeConstants();
  }

  @Override
  public void visitAssignmentOpExpr(AssignmentOpExpr assignmentOpExpr) throws IOException {
    throw shouldOnlyBeCalledForCompileTimeConstants();
  }

  @Override
  public void visitInfixOpExpr(InfixOpExpr infixOpExpr) throws IOException {
    visitBinaryOpExpr(infixOpExpr);
  }

  private void consumeRecordedAnnotations() {
    getCurrent(AnnotatedModel.class).setAnnotations(annotationModels);
    annotationModels = new ArrayList<AnnotationModel>();
  }

  private void recordAsdoc(JooSymbol symbol) {
    if (symbol != null) {
      String whitespace = symbol.getWhitespace();
      int startPos = whitespace.indexOf("/**");
      if (startPos != -1) {
        int endPos = whitespace.indexOf("*/", startPos);
        if (asdoc.length() > 0) {
          asdoc.append('\n'); // avoid missing white-space between two ASDoc sections
        }
        asdoc.append(whitespace.substring(startPos + 2, endPos - 1));
      }
    }
  }

  private void recordAsdoc(Declaration declaration) {
    for (JooSymbol symbol : declaration.getSymModifiers()) {
      recordAsdoc(symbol);
    }
  }

  private void consumeRecordedAsdoc() {
    ((DocumentedModel)modelStack.peek()).setAsdoc(trimAsdoc(asdoc.toString()));
    asdoc.setLength(0);
  }

  private void startRecordingCode() {
    code = new StringBuilder();
  }

  private void recordCode(JooSymbol symbol) {
    recordCode(symbol.getText()); // TODO: what about white-space?
  }

  private void recordCode(String text) {
    code.append(text);
  }

  private String consumeRecordedCode() {
    String recordedCode = code.toString();
    code = null;
    return recordedCode;
  }

  @Override
  public void visitAsExpr(AsExpr asExpr) throws IOException {
    visitInfixOpExpr(asExpr);
  }

  @Override
  public void visitArrayIndexExpr(ArrayIndexExpr arrayIndexExpr) throws IOException {
    throw shouldOnlyBeCalledForCompileTimeConstants();
  }

  @Override
  public void visitFunctionExpr(FunctionExpr functionExpr) throws IOException {
    throw shouldOnlyBeCalledForCompileTimeConstants();
  }

  @Override
  public void visitVectorLiteral(VectorLiteral vectorLiteral) throws IOException {
    throw shouldOnlyBeCalledForCompileTimeConstants();
  }

  @Override
  public void visitApplyExpr(ApplyExpr applyExpr) throws IOException {
    throw shouldOnlyBeCalledForCompileTimeConstants();
  }

  @Override
  public void visitNewExpr(NewExpr newExpr) throws IOException {
    throw shouldOnlyBeCalledForCompileTimeConstants();
  }

  @Override
  public void visitClassBody(ClassBody classBody) throws IOException {
    for (Directive directive : classBody.getDirectives()) {
      directive.visit(this);
    }
  }

  @Override
  public void visitBlockStatement(BlockStatement blockStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitDefaultStatement(DefaultStatement defaultStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitLabeledStatement(LabeledStatement labeledStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitIfStatement(IfStatement ifStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitCaseStatement(CaseStatement caseStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitTryStatement(TryStatement tryStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitCatch(Catch aCatch) throws IOException {
    throw new IllegalStateException("should not occur, because we are omitting try statements");
  }

  @Override
  public void visitForInStatement(ForInStatement forInStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitWhileStatement(WhileStatement whileStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitForStatement(ForStatement forStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitDoStatement(DoStatement doStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitSwitchStatement(SwitchStatement switchStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitSemicolonTerminatedStatement(SemicolonTerminatedStatement semicolonTerminatedStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitContinueStatement(ContinueStatement continueStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitBreakStatement(BreakStatement breakStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitThrowStatement(ThrowStatement throwStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitReturnStatement(ReturnStatement returnStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitEmptyStatement(EmptyStatement emptyStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitEmptyDeclaration(EmptyDeclaration emptyDeclaration) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  protected void visitIfNotNull(AstNode args) throws IOException {
    if (args != null) {
      args.visit(this);
    }
  }

  @Override
  public void visitParameter(Parameter parameter) throws IOException {
    ParamModel paramModel = new ParamModel();
    paramModel.setRest(parameter.isRest());
    modelStack.push(paramModel);
    parameter.getIde().visit(this);
    visitIfNotNull(parameter.getOptTypeRelation());
    visitIfNotNull(parameter.getOptInitializer());
    modelStack.pop();
    ((MethodModel)modelStack.peek()).addParam(paramModel);
  }

  private void generateVisibility(Declaration declaration) {
    NamespacedModel namespacedModel = (NamespacedModel)modelStack.peek();
    if (namespacedModel instanceof MemberModel) {
      ((MemberModel)namespacedModel).setStatic(declaration.isStatic());
    }
    // Public API only, thus either "protected", "public", or custom namespace:
    if (declaration instanceof TypedIdeDeclaration) {
      Ide namespace = ((TypedIdeDeclaration)declaration).getNamespace();
      if (namespace != null) {
        namespacedModel.setNamespace(namespace.getQualifiedNameStr());
        return;
      }
    }
    namespacedModel.setNamespace(declaration.isProtected() ? NamespacedModel.PROTECTED : NamespacedModel.PUBLIC);
  }

  private void generateStaticFlag(Declaration declaration) {
    MemberModel memberModel = (MemberModel)modelStack.peek();
    memberModel.setStatic(declaration.isStatic());
  }

  private void generateMemberModifiers(Declaration declaration) {
    generateVisibility(declaration);
    generateStaticFlag(declaration);
  }

  @Override
  public void visitVariableDeclaration(VariableDeclaration variableDeclaration) throws IOException {
    boolean isTopLevelDeclaration = modelStack.peek() instanceof CompilationUnitModel;
    if (variableDeclaration.isPublicApi() || isTopLevelDeclaration) {
      FieldModel fieldModel = new FieldModel();
      modelStack.push(fieldModel);
      consumeRecordedAnnotations();
      if (isTopLevelDeclaration) {
        handleExcludeClassByDefault(fieldModel);
      }
      recordAsdoc(variableDeclaration);
      recordAsdoc(variableDeclaration.getOptSymConstOrVar());
      consumeRecordedAsdoc();
      generateMemberModifiers(variableDeclaration);
      fieldModel.setConst(variableDeclaration.isConst());
      variableDeclaration.getIde().visit(this);
      TypeRelation optTypeRelation = variableDeclaration.getOptTypeRelation();
      if (optTypeRelation != null) {
        optTypeRelation.visit(this);
      }
      if (variableDeclaration.isConst()) {
        visitIfNotNull(variableDeclaration.getOptInitializer());
      }
      popMember();
      visitIfNotNull(variableDeclaration.getOptNextVariableDeclaration());
    }
  }

  @Override
  public void visitFunctionDeclaration(FunctionDeclaration functionDeclaration) throws IOException {
    boolean isTopLevelDeclaration = modelStack.peek() instanceof CompilationUnitModel;
    if (functionDeclaration.isPublicApi() || isTopLevelDeclaration) {
      MethodModel methodModel = new MethodModel();
      modelStack.push(methodModel);
      consumeRecordedAnnotations();
      if (isTopLevelDeclaration) {
        handleExcludeClassByDefault(methodModel);
      }
      recordAsdoc(functionDeclaration);
      recordAsdoc(functionDeclaration.getSymbol());
      consumeRecordedAsdoc();
      generateMemberModifiers(functionDeclaration);
      methodModel.setOverride(functionDeclaration.isOverride());
      methodModel.setFinal(functionDeclaration.isFinal());
      methodModel.setMethodType(functionDeclaration.isGetter() ? MethodType.GET
        : functionDeclaration.isSetter() ? MethodType.SET
        : null);
      functionDeclaration.getIde().visit(this);
      generateSignatureAsApiCode(functionDeclaration.getFun());

      if (functionDeclaration.isConstructor() && !functionDeclaration.isNative()) {
        // ASDoc does not allow a native constructor if the super class constructor needs parameters!
        StringBuilder superCallCode = new StringBuilder();
        superCallCode.append("super(");
        if (functionDeclaration.getClassDeclaration() != null) {
          ClassDeclaration superType = functionDeclaration.getClassDeclaration().getSuperTypeDeclaration();
          if (superType != null) {
            FunctionDeclaration superConstructor = superType.getConstructor();
            if (superConstructor != null) {
              Parameters superParameters = superConstructor.getParams();
              boolean first = true;
              while (superParameters != null && superParameters.getHead().getOptInitializer() == null) {
                if (first) {
                  first = false;
                } else {
                  superCallCode.append(", ");
                }
                superCallCode.append(VariableDeclaration.getDefaultValue(superParameters.getHead().getOptTypeRelation()));
                superParameters = superParameters.getTail();
              }
            }
          }
        }
        superCallCode.append(");");
        ((MethodModel)modelStack.peek()).setBody(superCallCode.toString());
      }
      popMember();
    }
  }

  private void popMember() {
    MemberModel member = (MemberModel)modelStack.pop();
    ActionScriptModel current = modelStack.peek();
    if (current instanceof ClassModel) {
      ((ClassModel)current).addMember(member);
    } else if (current instanceof CompilationUnitModel) {
      ((CompilationUnitModel)current).setPrimaryDeclaration(member);
    } else {
      throw new IllegalArgumentException("Members outside class or package not allowed.");
    }
  }

  public void generateSignatureAsApiCode(FunctionExpr fun) throws IOException {
    visitIfNotNull(fun.getParams());
    visitIfNotNull(fun.getOptTypeRelation());
  }

  protected void visitAll(Iterable<? extends AstNode> nodes) throws IOException {
    for (AstNode node : nodes) {
      node.visit(this);
    }
  }

  @Override
  public void visitClassDeclaration(ClassDeclaration classDeclaration) throws IOException {
    ClassModel classModel = new ClassModel();
    getCurrent(CompilationUnitModel.class).setPrimaryDeclaration(classModel);
    modelStack.push(classModel);
    recordAsdoc(classDeclaration);
    recordAsdoc(classDeclaration.getSymClass());
    consumeRecordedAsdoc();
    classModel.setFinal(classDeclaration.isFinal());
    classModel.setDynamic(classDeclaration.isDynamic());
    generateVisibility(classDeclaration);
    consumeRecordedAnnotations();
    handleExcludeClassByDefault(classModel);
    classDeclaration.getIde().visit(this);
    classModel.setInterface(classDeclaration.isInterface());
    visitIfNotNull(classDeclaration.getOptExtends());
    visitIfNotNull(classDeclaration.getOptImplements());
    classDeclaration.getBody().visit(this);
    modelStack.pop();
  }

  private void handleExcludeClassByDefault(AnnotatedModel annotatedModel) {
    if (isExcludeClassByDefault()) {
      // Add an [ExcludeClass] annotation, unless
      boolean needsExcludeClassAnnotation = true;
      for (AnnotationModel annotationModel : annotatedModel.getAnnotations()) {
        String metaName = annotationModel.getName();
        // ... an [PublicApi] or [ExcludeClass] annotation is already present.
        needsExcludeClassAnnotation = needsExcludeClassAnnotation &&
                !Jooc.PUBLIC_API_INCLUSION_ANNOTATION_NAME.equals(metaName) &&
                !Jooc.PUBLIC_API_EXCLUSION_ANNOTATION_NAME.equals(metaName);
      }
      if (needsExcludeClassAnnotation) {
        annotatedModel.addAnnotation(new AnnotationModel(Jooc.PUBLIC_API_EXCLUSION_ANNOTATION_NAME));
      }
    }
  }

  @Override
  public void visitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) throws IOException {
    NamespaceModel namespaceModel = new NamespaceModel();
    modelStack.push(namespaceModel);
    consumeRecordedAnnotations();
    handleExcludeClassByDefault(namespaceModel);
    recordAsdoc(namespaceDeclaration);
    recordAsdoc(namespaceDeclaration.getSymNamespace());
    consumeRecordedAsdoc();
    generateVisibility(namespaceDeclaration);
    namespaceDeclaration.getIde().visit(this);
    visitIfNotNull(namespaceDeclaration.getOptInitializer());
    modelStack.pop();
    getCurrent(CompilationUnitModel.class).setPrimaryDeclaration(namespaceModel);
  }

  @Override
  public void visitPackageDeclaration(PackageDeclaration packageDeclaration) throws IOException {
    Ide packageIde = packageDeclaration.getIde();
    if (packageIde != null) {
      getCurrent(CompilationUnitModel.class).setPackage(packageIde.getQualifiedNameStr());
    }
  }

  @Override
  public void visitSuperConstructorCallStatement(SuperConstructorCallStatement superConstructorCallStatement) throws IOException {
    // This may appear as a class initializer, in which case we can simply skip it.
  }

  @Override
  public void visitAnnotation(Annotation annotation) throws IOException {
    AnnotationModel annotationModel = new AnnotationModel();
    modelStack.push(annotationModel);
    recordAsdoc(annotation.getLeftBracket());
    consumeRecordedAsdoc();
    annotation.getIde().visit(this);
    visitIfNotNull(annotation.getOptAnnotationParameters());
    modelStack.pop();
    annotationModels.add(annotationModel);
  }

  @Override
  public void visitUseNamespaceDirective(UseNamespaceDirective useNamespaceDirective) throws IOException {
    // no api code generated
  }

  @Override
  public void visitImportDirective(ImportDirective importDirective) throws IOException {
    // no api code generated
  }

  @Override
  public void visitLiteralExpr(LiteralExpr literalExpr) throws IOException {
    recordCode(literalExpr.getValue());
  }

  @Override
  public void visitPostfixOpExpr(PostfixOpExpr postfixOpExpr) throws IOException {
    postfixOpExpr.getArg().visit(this);
    recordCode(postfixOpExpr.getOp());
  }

  @Override
  public void visitDotExpr(DotExpr dotExpr) throws IOException {
    dotExpr.getArg().visit(this);
    recordCode(dotExpr.getOp());
    recordCode(dotExpr.getIde().getName());
  }

  @Override
  public void visitPrefixOpExpr(PrefixOpExpr prefixOpExpr) throws IOException {
    recordCode(prefixOpExpr.getOp());
    prefixOpExpr.getArg().visit(this);
  }

  @Override
  public void visitBinaryOpExpr(BinaryOpExpr binaryOpExpr) throws IOException {
    binaryOpExpr.getArg1().visit(this);
    recordCode(binaryOpExpr.getOp());
    binaryOpExpr.getArg2().visit(this);
  }

  @Override
  public void visitIsExpr(IsExpr isExpr) throws IOException {
    visitBinaryOpExpr(isExpr);
  }

  @Override
  public void visitConditionalExpr(ConditionalExpr conditionalExpr) throws IOException {
    conditionalExpr.getCond().visit(this);
    recordCode(conditionalExpr.getSymQuestion());
    conditionalExpr.getIfTrue().visit(this);
    recordCode(conditionalExpr.getSymColon());
    conditionalExpr.getIfFalse().visit(this);
  }

  @Override
  public <T extends AstNode> void visitCommaSeparatedList(CommaSeparatedList<T> commaSeparatedList) throws IOException {
    visitIfNotNull(commaSeparatedList.getHead());
    if (commaSeparatedList.getSymComma() != null) {
      visitIfNotNull(commaSeparatedList.getTail());
    }
  }

  @Override
  public void visitParameters(Parameters parameters) throws IOException {
    visitCommaSeparatedList(parameters);
  }

  @Override
  public void visitPredefinedTypeDeclaration(PredefinedTypeDeclaration predefinedTypeDeclaration) throws IOException {
    throw new IllegalStateException("there should be no code generation for predefined types");
  }

  private <T extends ActionScriptModel> T getCurrent(Class<T> targetClass) {
    return targetClass.cast(modelStack.peek());
  }
 
}

  private static String trimAsdoc(String asdoc) {
    int oldLength;
    do {
      oldLength = asdoc.length();
      asdoc = asdoc.trim();
      if (asdoc.startsWith("*")) {
        asdoc = asdoc.substring(1);
      }
      if (asdoc.endsWith("*")) {
        asdoc = asdoc.substring(0, asdoc.length() - 1);
      }
    } while (asdoc.length() < oldLength);
    return asdoc;
  }

}
TOP

Related Classes of net.jangaroo.jooc.backend.ApiModelGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.