// Copyright (C) 2009 Google Inc. // // 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 com.google.caja.parser.js; import com.google.caja.lexer.FilePosition; import com.google.caja.lexer.TokenConsumer; import com.google.caja.parser.AbstractParseTreeNode; import com.google.caja.render.Concatenator; import com.google.caja.render.JsPrettyPrinter; import com.google.caja.util.Callback; import java.io.IOException; import java.util.List; /** * The result of running the cajoler over some content. * * @author ihab.awad@gmail.com */ public final class CajoledModule extends AbstractParseTreeNode { private static final long serialVersionUID = -2499144011243193616L; /** @param value unused. This ctor is provided for reflection. */ @ReflectiveCtor public CajoledModule(FilePosition pos, Void value, List<? extends ObjectConstructor> children) { this(pos, children.get(0)); assert children.size() == 1; } /** * Creates a CajoledModule. * * @param pos a file position. * @param body an object constructor representing the module. */ public CajoledModule(FilePosition pos, ObjectConstructor body) { super(pos, ObjectConstructor.class); createMutation().appendChild(body).execute(); } /** * Creates a CajoledModule. * * @param body an object constructor representing the module. */ public CajoledModule(ObjectConstructor body) { this(body.getFilePosition(), body); } @Override protected void childrenChanged() { super.childrenChanged(); if (children().size() != 1 && getModuleBody() != null) { throw new IllegalStateException( "A CajoledModule may only have one child"); } } @Override public Object getValue() { return null; } @Override public List<? extends ObjectConstructor> children() { return childrenAs(ObjectConstructor.class); } public ObjectConstructor getModuleBody() { return children().get(0); } /** The URI from which the module was loaded. */ public String getSrc() { ValueProperty p = (ValueProperty) getModuleBody().propertyWithName("src"); if (p == null) { return null; } return ((StringLiteral) p.getValueExpr()).getUnquotedValue(); } /** The URIs of modules needed by this module. */ public ArrayConstructor getIncludedModules() { ValueProperty p = (ValueProperty) getModuleBody().propertyWithName( "includedModules"); return p != null ? (ArrayConstructor) p.getValueExpr() : null; } /** The URIs of modules inlined by this module. */ public ArrayConstructor getInlinedModules() { ValueProperty p = (ValueProperty) getModuleBody().propertyWithName( "inlinedModules"); return p != null ? (ArrayConstructor) p.getValueExpr() : null; } public FunctionConstructor getInstantiateMethod() { return (FunctionConstructor) ((ValueProperty) getModuleBody().propertyWithName("instantiate")).getValueExpr(); } public final TokenConsumer makeRenderer( Appendable out, Callback<IOException> exHandler) { return new JsPrettyPrinter(new Concatenator(out, exHandler)); } /** * Returns a flattened copy of this CajoledModule. The flattened copy * is cheaper to clone and/or cache. * <p> * Moderately-large JS (such as jquery.js) becomes a large ParseTreeNode * structure that takes a nontrivial amount of time to clone or * deserialize. However, once we've finished cajoling a module, * the tree structure becomes irrelevant, so we can render the * module body into a single string for efficiency. * <p> * This requires making an early decision on what renderer to use. */ public CajoledModule flatten(boolean minify) { ObjectConstructor oc = new ObjectConstructor(FilePosition.UNKNOWN); for (ObjProperty p : getModuleBody().children()) { oc.appendChild(flattenProperty(p, minify)); } return new CajoledModule(oc); } private ObjProperty flattenProperty(ObjProperty op, boolean minify) { if (op instanceof ValueProperty) { ValueProperty vp = (ValueProperty) op; String name = vp.getPropertyName(); if ("instantiate".equals(name)) { return new ValueProperty( vp.getPropertyNameNode(), new RenderedExpression(vp.getValueExpr(), minify)); } } return op; } }