All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jamon.codegen.TemplateUnit Maven / Gradle / Ivy

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.jamon.codegen;

import java.security.MessageDigest;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import org.jamon.api.Location;
import org.jamon.compiler.ParserErrorsImpl;
import org.jamon.node.AnnotationNode;
import org.jamon.node.ClassNode;
import org.jamon.node.GenericsParamNode;
import org.jamon.node.ImportNode;
import org.jamon.node.ParentArgNode;
import org.jamon.node.StaticImportNode;
import org.jamon.util.StringUtils;

public class TemplateUnit extends AbstractUnit implements InheritedUnit {
  private InheritedArgs inheritedArgs = null;
  private TemplateDescription parentDescription = TemplateDescription.EMPTY;
  private final List declaredRequiredArgs = new LinkedList();
  private final List fragmentArgs = new LinkedList();
  private final Set declaredOptionalArgs = new TreeSet();
  private final Set declaredFragmentArgs = new TreeSet();
  private final Map defs = new TreeMap();
  private final Map methods = new TreeMap();
  private final List overrides = new LinkedList();
  private final List imports = new LinkedList();
  private final List staticImports = new LinkedList();
  private final List interfaces = new LinkedList();

  private String parentPath;
  private boolean isParent = false;
  private String replacedTemplatePath;
  private TemplateDescription replacedTemplateDescription;
  private boolean replaceable = false;
  private final List classContent = new LinkedList();
  private final Set dependencies = new HashSet();
  private final Set callNames = new HashSet();
  private final Collection abstractMethodNames = new TreeSet();
  private final GenericParams genericParams = new GenericParams();
  private String jamonContextType;
  private final List annotations = new LinkedList();
  private String encoding;

  public TemplateUnit(String path, ParserErrorsImpl errors) {
    super(path, null, errors, null);
  }

  public int getInheritanceDepth() {
    return parentDescription == null
        ? 0
        : 1 + parentDescription.getInheritanceDepth();
  }

  /**
   * public for unit testing purposes
   **/

  public void setParentDescription(TemplateDescription parent) {
    parentDescription = parent;
    // FIXME - join them later.
    fragmentArgs.addAll(parent.getFragmentInterfaces());

    inheritedArgs = new InheritedArgs(
      getParentPath(),
      parent.getRequiredArgs(),
      parent.getOptionalArgs(),
      parent.getFragmentInterfaces(),
      getErrors());

    for (AbstractArgument arg : new Concatenation(parent.getRequiredArgs(),
        parent.getOptionalArgs(), parent.getFragmentInterfaces())) {
      addArgName(arg);
    }

    callNames.addAll(parent.getMethodUnits().keySet());
    abstractMethodNames.addAll(parent.getAbstractMethodNames());
    if (jamonContextType == null) {
      setJamonContextType(parent.getJamonContextType());
    }
  }

  @Override
  public void addParentArg(ParentArgNode arg) {
    inheritedArgs.addParentArg(arg);
  }

  @Override
  public List getFragmentArgs() {
    return fragmentArgs;
  }

  @Override
  public void addFragmentArg(FragmentArgument arg) {
    fragmentArgs.add(arg);
    declaredFragmentArgs.add(arg);
  }

  public Collection getDeclaredFragmentArgs() {
    return declaredFragmentArgs;
  }

  @Override
  public void addRequiredArg(RequiredArgument arg) {
    declaredRequiredArgs.add(arg);
  }

  @Override
  public void addOptionalArg(OptionalArgument arg) {
    declaredOptionalArgs.add(arg);
  }

  @Override
  public List getSignatureRequiredArgs() {
    return new SequentialList(
        parentDescription.getRequiredArgs(), declaredRequiredArgs);
  }

  @Override
  public Collection getSignatureOptionalArgs() {
    return new Concatenation(
        parentDescription.getOptionalArgs(), declaredOptionalArgs);
  }

  public String getOptionalArgDefault(OptionalArgument arg) {
    return declaredOptionalArgs.contains(arg)
        ? arg.getDefault()
        : inheritedArgs.getDefaultValue(arg);
  }

  @Override
  public Collection getVisibleArgs() {
    return inheritedArgs == null
        ? new Concatenation(getDeclaredRenderArgs(), getDeclaredOptionalArgs())
        : new Concatenation(
            inheritedArgs.getVisibleArgs(), getDeclaredRenderArgs(), getDeclaredOptionalArgs());
  }

  public Collection getDeclaredOptionalArgs() {
    return declaredOptionalArgs;
  }

  public Collection getTemplateDependencies() {
    return dependencies;
  }

  private void checkCallName(String name, Location location) {
    if (!callNames.add(name)) {
      getErrors().addError("multiple defs and/or methods named " + name, location);
    }
  }

  public void makeDefUnit(String defName, Location location) {
    checkCallName(defName, location);
    defs.put(defName, new DefUnit(defName, this, getErrors(), location));
  }

  public Collection getDefUnits() {
    return defs.values();
  }

  public DefUnit getDefUnit(String name) {
    return defs.get(name);
  }

  public void makeMethodUnit(String methodName, Location location, boolean isAbstract) {
    checkCallName(methodName, location);
    methods.put(methodName, new DeclaredMethodUnit(methodName, this, getErrors(), location,
        isAbstract));
    if (isAbstract) {
      abstractMethodNames.add(methodName);
    }
  }

  public OverriddenMethodUnit makeOverridenMethodUnit(String name, Location location) {
    DeclaredMethodUnit methodUnit = (DeclaredMethodUnit) parentDescription.getMethodUnits().get(
      name);
    if (methodUnit == null) {
      getErrors().addError("There is no such method " + name + " to override", location);
      // Provide a dummy parent, to allow us to catch errors inside the
      // override
      methodUnit = new DeclaredMethodUnit(name, this, getErrors(), location);
    }

    abstractMethodNames.remove(name);
    OverriddenMethodUnit override =
      new OverriddenMethodUnit(methodUnit, this, getErrors(), location);
    overrides.add(override);
    return override;
  }

  public MethodUnit getMethodUnit(String name) {
    return (methods.containsKey(name)
        ? methods.get(name)
        : parentDescription.getMethodUnits().get(name));
  }

  public Collection getSignatureMethodUnits() {
    return new Concatenation(
        getDeclaredMethodUnits(), parentDescription.getMethodUnits().values());
  }

  public Collection getDeclaredMethodUnits() {
    return methods.values();
  }

  public Collection getImplementedMethodUnits() {
    return new Concatenation(getDeclaredMethodUnits(), overrides);
  }

  public Collection getAbstractMethodNames() {
    return abstractMethodNames;
  }

  private Iterable getImports() {
    return imports;
  }

  public void addStaticImport(StaticImportNode node) {
    staticImports.add(node);
  }

  private Iterable getStaticImports() {
    return staticImports;
  }

  public void addImport(ImportNode node) {
    imports.add(node);
  }

  public void addInterface(String interfase) {
    interfaces.add(interfase);
  }

  public void setParentPath(String parentPath) {
    this.parentPath = parentPath;
    dependencies.add(parentPath);
  }

  public String getParentPath() {
    return parentPath;
  }

  public boolean hasParentPath() {
    return parentPath != null;
  }

  /**
   * Set the path of the template which this template replaces, along with the description of the
   * replaced template.
   *
   * @param replacedTemplatePath the path of the template which this template replaced
   * @param templateDescription the description of the replaced template
   */
  public void setReplacedTemplatePathAndDescription(
    String replacedTemplatePath, TemplateDescription templateDescription) {
    this.replacedTemplatePath = replacedTemplatePath;
    this.replacedTemplateDescription = templateDescription;
    dependencies.add(replacedTemplatePath);
  }

  /**
   * Get the path of the template which this template replaces, or null if it is not replacing a
   * template.
   *
   * @return the path of the template which this template replaces, or null if it is not replacing a
   *         template.
   */
  public String getReplacedTemplatePath() {
    return replacedTemplatePath;
  }

  /**
   * Get the description of the template this template is replacing, or null if it is not replacing
   * a template
   *
   * @return the description of the template this template is replacing, or null if it is not
   *         replacing a template
   */
  public TemplateDescription getReplacedTemplateDescription() {
    return replacedTemplateDescription;
  }

  /**
   * Whether this template replaces another template. This is the case if the template has a {@code
   * <%replacesTemplate ...>} tag.
   *
   * @return {@code true} if this template replaces another template.
   */
  public boolean isReplacing() {
    return replacedTemplatePath != null;
  }

  /**
   * Set whether this template can be replaced by another one.
   *
   * @param replaceable whether this template can be replaced by another one.
   */
  public void setReplaceable(boolean replaceable) {
    this.replaceable = replaceable;
  }

  /**
   * Get whether this template can be replaced by another one.
   *
   * @return whether this template can be replaced by another one.
   */
  public boolean isReplaceable() {
    return replaceable;
  }

  public String getProxyParentClass() {
    return hasParentPath()
        ? PathUtils.getFullyQualifiedIntfClassName(getParentPath())
        : ClassNames.TEMPLATE;
  }

  public boolean isParent() {
    return isParent;
  }

  public void setIsParent() {
    isParent = true;
  }

  public void printClassContent(CodeWriter writer) {
    for (ClassNode node : classContent) {
      writer.printLocation(node.getLocation());
      writer.println(node.getContent());
    }
  }

  public void addClassContent(ClassNode node) {
    classContent.add(node);
  }

  public void addCallPath(String callPath) {
    dependencies.add(callPath);
  }

  public Collection getParentRenderArgs() {
    return new Concatenation(
        parentDescription.getRequiredArgs(), parentDescription.getFragmentInterfaces());
  }

  public void printImports(CodeWriter writer) {
    for (ImportNode node : getImports()) {
      writer.printLocation(node.getLocation());
      writer.println("import " + node.getName() + ";");
    }
    for (StaticImportNode node : getStaticImports()) {
      writer.printLocation(node.getLocation());
      writer.println("import static " + node.getName() + ";");
    }
    writer.println();
  }

  public void printParentRenderArgs(CodeWriter writer) {
    printArgs(writer, getParentRenderArgs());
  }

  public void printParentRenderArgsDecl(CodeWriter writer) {
    printArgsDecl(writer, getParentRenderArgs());
  }

  public Collection getDeclaredRenderArgs() {
    return new Concatenation(declaredRequiredArgs, declaredFragmentArgs);
  }

  public Collection getDeclaredArgs() {
    return new Concatenation(getDeclaredRenderArgs(), declaredOptionalArgs);
  }

  public void printDeclaredRenderArgs(CodeWriter writer) {
    printArgs(writer, getDeclaredRenderArgs());
  }

  public void printDeclaredRenderArgsDecl(CodeWriter writer) {
    printArgsDecl(writer, getDeclaredRenderArgs());
  }

  public void printInterfaces(CodeWriter writer) {
    if (interfaces.size() > 0) {
      writer.print("  implements ");
      writer.openList("", false);
      for (String intrface : interfaces) {
        writer.printListElement(intrface);
      }
      writer.closeList("");
    }
  }

  @Override
  protected void generateInterfaceSummary(StringBuilder buf) {
    super.generateInterfaceSummary(buf);
    buf.append("GenericParams:");
    buf.append(getGenericParams().generateGenericsDeclaration());
    buf.append("\n");
    buf.append("replaceable:").append(isReplaceable()).append("\n");
    if (parentDescription != null) {
      buf.append("Parent sig: ").append(parentDescription.getSignature()).append("\n");
    }
    for (FragmentArgument arg : getFragmentArgs()) {
      buf.append("Fragment: ").append(arg.getName()).append("\n");
      arg.getFragmentUnit().generateInterfaceSummary(buf);
    }
  }

  /**
   * Get the signature hash for this template. The signature is a hash which will change in the
   * event that the template's API has changed.
   *
   * @return the signature hash for this template
   */
  public String getSignature() {
    try {
      StringBuilder buf = new StringBuilder();
      generateInterfaceSummary(buf);
      return StringUtils.byteArrayToHexString(
        MessageDigest.getInstance("MD5").digest(buf.toString().getBytes("UTF-8")));
    }
    catch (RuntimeException e) {
      throw e;
    }
    catch (Exception e) {
      throw new RuntimeException("Unable to generate signature", e);
    }
  }

  public GenericParams getGenericParams() {
    return genericParams;
  }

  public void setJamonContextType(String jamonContextType) {
    this.jamonContextType = jamonContextType;
  }

  public String getJamonContextType() {
    return jamonContextType;
  }

  public boolean isOriginatingJamonContext() {
    return jamonContextType != null
      && (!hasParentPath() || parentDescription.getJamonContextType() == null);
  }

  public void addGenericsParamNode(GenericsParamNode node) {
    genericParams.addParam(node);
  }

  public void addAnnotationNode(AnnotationNode node) {
    annotations.add(node);
  }

  public Iterable getAnnotations() {
    return annotations;
  }

  public void setEncoding(String encoding) {
    this.encoding = encoding;
  }

  public String getEncoding() {
    return encoding;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy