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

com.google.gxp.compiler.escape..svn.text-base.Escaper.svn-base Maven / Gradle / Ivy

Go to download

Google XML Pages (GXP) is a templating system used to generate XML/SGML markup (most often HTML).

The newest version!
/*
 * Copyright (C) 2008 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.gxp.compiler.escape;

import com.google.common.base.CharEscapers;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gxp.compiler.alerts.AlertSetBuilder;
import com.google.gxp.compiler.alerts.AlertSink;
import com.google.gxp.compiler.base.AbbrExpression;
import com.google.gxp.compiler.base.AttrBundleParam;
import com.google.gxp.compiler.base.BooleanConstant;
import com.google.gxp.compiler.base.BoundCall;
import com.google.gxp.compiler.base.Call;
import com.google.gxp.compiler.base.CallVisitor;
import com.google.gxp.compiler.base.Callable;
import com.google.gxp.compiler.base.CollapseExpression;
import com.google.gxp.compiler.base.Concatenation;
import com.google.gxp.compiler.base.Conditional;
import com.google.gxp.compiler.base.ConstructedConstant;
import com.google.gxp.compiler.base.ContentType;
import com.google.gxp.compiler.base.DefaultingTypeVisitor;
import com.google.gxp.compiler.base.EscapeExpression;
import com.google.gxp.compiler.base.ExhaustiveExpressionVisitor;
import com.google.gxp.compiler.base.Expression;
import com.google.gxp.compiler.base.ExtractedMessage;
import com.google.gxp.compiler.base.Parameter;
import com.google.gxp.compiler.base.NativeExpression;
import com.google.gxp.compiler.base.ObjectConstant;
import com.google.gxp.compiler.base.OutputElement;
import com.google.gxp.compiler.base.PlaceholderEnd;
import com.google.gxp.compiler.base.PlaceholderNode;
import com.google.gxp.compiler.base.PlaceholderStart;
import com.google.gxp.compiler.base.Root;
import com.google.gxp.compiler.base.StringConstant;
import com.google.gxp.compiler.base.Template;
import com.google.gxp.compiler.base.Type;
import com.google.gxp.compiler.base.TypeVisitor;
import com.google.gxp.compiler.base.UnboundCall;
import com.google.gxp.compiler.base.UnexpectedNodeException;
import com.google.gxp.compiler.base.UnextractedMessage;
import com.google.gxp.compiler.base.Util;
import com.google.gxp.compiler.base.ValidatedCall;
import com.google.gxp.compiler.phinsert.PlaceholderInsertedTree;
import com.google.gxp.compiler.reparent.Attribute;
import com.google.gxp.compiler.schema.ContentFamily;
import com.google.gxp.compiler.schema.ContentFamilyVisitor;
import com.google.gxp.compiler.schema.Schema;

import java.util.*;

/**
 * Escapes content in the tree.
 */
public class Escaper implements Function {

  /**
   * Escapes content in the supplied SpaceCollapsedTree to produce
   * an EscapedTree.
   */
  public EscapedTree apply(PlaceholderInsertedTree tree) {
    AlertSetBuilder alertSetBuilder = new AlertSetBuilder(tree.getAlerts());
    Worker worker = new Worker(alertSetBuilder);
    Root root = worker.apply(tree.getRoot());

    return new EscapedTree(tree.getSourcePosition(), alertSetBuilder.buildAndClear(), root);
  }

  private class Worker implements Function {
    private final AlertSink alertSink;

    Worker(AlertSink alertSink) {
      this.alertSink = Preconditions.checkNotNull(alertSink);
    }

    private final Map visitors = Maps.newHashMap();

    private Visitor visitor(Schema schema) {
      Visitor result = visitors.get(schema);
      if (result == null) {
        result = new Visitor(schema);
        visitors.put(schema, result);
      }
      return result;
    }

    public Root apply(Root root) {
      return (root.getSchema() == null)
          ? root
          : root.acceptVisitor(visitor(root.getSchema()));
    }

    private class Visitor
        extends ExhaustiveExpressionVisitor
        implements CallVisitor {

      private final Schema schema;

      // used to keep track of what Attribute (if any) we are inside of
      private final Deque attrStack = new ArrayDeque();

      Visitor(Schema schema) {
        this.schema = Preconditions.checkNotNull(schema);
      }

      @Override
      protected Expression postProcess(Expression result) {
        if (!schema.allows(result.getSchema())) {
          alertSink.add(new TypeError(schema, result));
          return new StringConstant(result, schema, "");
        } else {
          return result;
        }
      }

      @Override
      public Template visitTemplate(Template template) {
        Template result = super.visitTemplate(template);
        if (!schema.allows(result.getContent().getSchema())) {
          throw new AssertionError();
        }
        return result;
      }

      private final Function parameterTransformer =
        new Function() {
          public Parameter apply(Parameter param) {
            Expression defaultValue = param.getDefaultValue();
            if (defaultValue != null) {
              Visitor valueVisitor = param.getType().acceptTypeVisitor(typeVisitor);
              param = param.withDefaultValue(defaultValue.acceptVisitor(valueVisitor));
            }
            return param.withComment(
                param.getComment().acceptVisitor(Visitor.this));
          }
        };

      @Override
      protected Function getParameterTransformer() {
        return parameterTransformer;
      }

      @Override
      public Expression visitAbbrExpression(AbbrExpression node) {
        // if the gxp:abbr specifies a content-type we need a visitor using the
        // appropriate Schema to visit the value
        Visitor valueVisitor = node.getType().acceptTypeVisitor(typeVisitor);
        return postProcess(node.withValueAndContent(valueVisitor.apply(node.getValue()),
                                                    apply(node.getContent())));
      }

      @Override
      public Expression visitStringConstant(StringConstant node) {
        ContentFamily contentFamily = schema.getContentFamily();
        return postProcess(
            new StringConstant(
                node, schema,
                contentFamily.acceptVisitor(STATIC_ESCAPE_VISITOR, node.evaluate())));
      }

      @Override
      public Expression visitAttrBundleParam(AttrBundleParam node) {
        return postProcess(node.transform(visitor(node.getSchema())));
      }

      @Override
      public Expression visitBooleanConstant(BooleanConstant node) {
        return postProcess(new EscapeExpression(schema, node));
      }

      @Override
      public Expression visitObjectConstant(ObjectConstant node) {
        return postProcess(new EscapeExpression(schema, node));
      }

      @Override
      public Expression visitConstructedConstant(ConstructedConstant node) {
        return postProcess(new EscapeExpression(schema, node));
      }

      @Override
      public Expression visitNativeExpression(NativeExpression node) {
        return postProcess(new EscapeExpression(schema, node));
      }

      @Override
      public Expression visitConcatenation(Concatenation node) {
        return postProcess(Concatenation.create(node.getSourcePosition(),
                                                schema,
                                                Util.map(node.getValues(),
                                                         this)));
      }

      @Override
      public Expression visitOutputElement(OutputElement element) {
        Schema innerSchema = element.getInnerSchema();
        Visitor contentVisitor = (innerSchema == null)
            ? this : visitor(innerSchema);
        return postProcess(
            element.withAttributesAndContent(
                Util.map(element.getAttributes(), getAttributeFunction()),
                element.getContent().acceptVisitor(contentVisitor)));
      }

      @Override
      public Attribute visitAttribute(Attribute attr) {
        attrStack.push(attr);
        Schema schema = attr.getInnerSchema();
        if (schema != null) {
          Visitor visitor = visitor(schema);
          attr = attr.withValue(attr.getValue().acceptVisitor(visitor));
        }

        Attribute result = super.visitAttribute(attr);
        attrStack.pop();
        return result;
      }

      @Override
      public Expression visitConditional(Conditional conditional) {
        List clauses = Lists.newArrayList();
        for (Conditional.Clause clause : conditional.getClauses()) {
          clauses.add(visitClause(clause));
        }
        Expression elseExpression = apply(conditional.getElseExpression());
        return postProcess(conditional.withSchemaAndClauses(schema, clauses, elseExpression));
      }

      @Override
      public Expression visitUnextractedMessage(UnextractedMessage msg) {

        // if  didn't have a content-type attribute,
        // it takes on the schema of its surrounding context
        Schema msgSchema = msg.getSchema();
        if (msgSchema == null) {
          msgSchema = schema.getMsgSchema();
        }

        Expression result = msg.withContentAndSchema(
            msg.getContent().acceptVisitor(visitor(msgSchema)),
            msgSchema);

        if (!result.getSchema().isTranslatable()) {
          alertSink.add(new UntranslatableMsgError(result));
        }

        return postProcess(
            result.getSchema().equals(schema)
              ? result
              : new EscapeExpression(schema, result));
      }

      @Override
      public Expression visitCall(Call call) {
        return postProcess(call.acceptCallVisitor(this));
      }

      private TypeVisitor typeVisitor =
        new DefaultingTypeVisitor() {
          protected Visitor defaultVisitType(Type type) {
            return Visitor.this;
          }

          public Visitor visitContentType(ContentType type) {
            return visitor(type.getSchema());
          }
        };

      public Expression visitBoundCall(BoundCall call) {
        Callable callee = call.getCallee();

        // tranform params
        ImmutableMap.Builder newParams = ImmutableMap.builder();

        for (Map.Entry p : call.getAttributes().entrySet()) {
          String key = p.getKey();
          Visitor visitor = callee
              .getParameterByPrimary(key).getType()
              .acceptTypeVisitor(typeVisitor);
          Attribute value = visitor.visitAttribute(p.getValue());
          newParams.put(key, value);
        }

        Expression result = call.withParams(newParams.build());

        // If we are inside of an attribute with a content-type that matches the
        // content-type of the call, we'll need to escape the results into the
        // content-type of the element
        if (!attrStack.isEmpty()
            && !result.getSchema().equals(schema)
            && result.getSchema().equals(attrStack.peek().getInnerSchema())) {
          result = new EscapeExpression(schema, result);
        }

        return result;
      }

      public Expression visitUnboundCall(UnboundCall call) {
        throw new UnexpectedNodeException(call);
      }

      public Expression visitValidatedCall(ValidatedCall call) {
        throw new UnexpectedNodeException(call);
      }

      @Override
      public Expression visitPlaceholderStart(PlaceholderStart ph) {
        return postProcess(ph.withSchema(schema));
      }

      @Override
      public Expression visitPlaceholderEnd(PlaceholderEnd eph) {
        return postProcess(eph.withSchema(schema));
      }

      @Override
      public Expression visitEscapeExpression(EscapeExpression node) {
        return postProcess(new EscapeExpression(schema, node));
      }

      @Override
      public Expression visitCollapseExpression(CollapseExpression node) {
        throw new UnexpectedNodeException(node);
      }

      @Override
      public Expression visitExtractedMessage(ExtractedMessage node) {
        throw new UnexpectedNodeException(node);
      }

      @Override
      public Expression visitPlaceholderNode(PlaceholderNode node) {
        throw new UnexpectedNodeException(node);
      }
    }
  }

  private static final ContentFamilyVisitor
      STATIC_ESCAPE_VISITOR =
        new ContentFamilyVisitor() {
          public String visitMarkup(String s) {
            // TODO(laurence): what about non-HTML markup???
            return CharEscapers.htmlEscaper().escape(s);
          }

          public String visitJavaScript(String s) {
            return s;
          }

          public String visitCss(String s) {
            return s;
          }

          public String visitPlaintext(String s) {
            return s;
          }
        };
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy