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

com.google.gxp.compiler.msgextract..svn.text-base.MessageExtractor.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.msgextract;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.gxp.compiler.alerts.AlertSetBuilder;
import com.google.gxp.compiler.alerts.AlertSink;
import com.google.gxp.compiler.alerts.SourcePosition;
import com.google.gxp.compiler.alerts.common.BadNodePlacementError;
import com.google.gxp.compiler.alerts.common.InvalidMessageError;
import com.google.gxp.compiler.base.CollapseExpression;
import com.google.gxp.compiler.base.Concatenation;
import com.google.gxp.compiler.base.ConvertibleToContent;
import com.google.gxp.compiler.base.DefaultingExpressionVisitor;
import com.google.gxp.compiler.base.ExampleExpression;
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.NoMessage;
import com.google.gxp.compiler.base.Node;
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.UnexpectedNodeException;
import com.google.gxp.compiler.base.UnextractedMessage;
import com.google.gxp.compiler.i18ncheck.I18nCheckedTree;
import com.google.transconsole.common.messages.InvalidMessageException;
import com.google.transconsole.common.messages.Message;
import com.google.transconsole.common.messages.MessageBuilder;

import java.util.List;

/**
 * Replaces message elements and contained Output nodes with validated message
 * objects that hold message data as well as a set of DynamicPlaceholder
 * children. For example:
 *
 * 
*/ public class MessageExtractor implements Function { public MessageExtractedTree apply(I18nCheckedTree tree) { List messages = Lists.newArrayList(); AlertSetBuilder alertSetBuilder = new AlertSetBuilder(tree.getAlerts()); Root root = tree.getRoot().acceptVisitor(new OutsideMessageVisitor(alertSetBuilder, messages)); return new MessageExtractedTree(tree.getSourcePosition(), alertSetBuilder.buildAndClear(), root, messages); } /** * Visitor used when we are not inside of a {@code } or * {@code } element. */ private static class OutsideMessageVisitor extends ExhaustiveExpressionVisitor { private final AlertSink alertSink; private final List messages; OutsideMessageVisitor(AlertSink alertSink, List messages) { this.alertSink = Preconditions.checkNotNull(alertSink); this.messages = Preconditions.checkNotNull(messages); } public void addMessages(ExtractedMessage message) { messages.add(message); } @Override public Expression visitNoMessage(NoMessage noMsg) { InsideNoMessageVisitor insideNoMsgVisitor = new InsideNoMessageVisitor(alertSink, noMsg, this); return noMsg.getSubexpression().acceptVisitor(insideNoMsgVisitor); } @Override public Expression visitUnextractedMessage(UnextractedMessage msg) { InsideMessageVisitor subVisitor = new InsideMessageVisitor(alertSink, this, msg); msg.getContent().acceptVisitor(subVisitor); return subVisitor.getResult(); } @Override public Expression visitPlaceholderNode(PlaceholderNode ph) { alertSink.add(new BadNodePlacementError(ph, null)); return ph.getContent().acceptVisitor(this); } @Override public Expression visitPlaceholderStart(PlaceholderStart value) { throw new UnexpectedNodeException(value); } @Override public Expression visitPlaceholderEnd(PlaceholderEnd value) { throw new UnexpectedNodeException(value); } } /** * Visitor used when we are not inside of a {@code } or * {@code } element. */ private static class InsideNoMessageVisitor extends ExhaustiveExpressionVisitor { private final AlertSink alertSink; private final NoMessage rootNoMsg; private final OutsideMessageVisitor outsideMessageVisitor; InsideNoMessageVisitor(AlertSink alertSink, NoMessage rootNoMsg, OutsideMessageVisitor outsideMessageVisitor) { this.alertSink = Preconditions.checkNotNull(alertSink); this.rootNoMsg = Preconditions.checkNotNull(rootNoMsg); this.outsideMessageVisitor = Preconditions.checkNotNull(outsideMessageVisitor); } @Override public Expression visitUnextractedMessage(UnextractedMessage msg) { alertSink.add(new BadNodePlacementError(msg, rootNoMsg)); return msg.acceptVisitor(outsideMessageVisitor); } @Override public Expression visitNoMessage(NoMessage noMsg) { alertSink.add(new BadNodePlacementError(noMsg, rootNoMsg)); return noMsg.getSubexpression().acceptVisitor(this); } @Override public Expression visitPlaceholderNode(PlaceholderNode ph) { alertSink.add(new BadNodePlacementError(ph, rootNoMsg)); return ph.getContent().acceptVisitor(this); } @Override public Expression visitPlaceholderStart(PlaceholderStart value) { throw new UnexpectedNodeException(value); } @Override public Expression visitPlaceholderEnd(PlaceholderEnd value) { throw new UnexpectedNodeException(value); } } /** * Visitor used when we are inside of a {@code } element. * The calling protocol is to create the InsideMessageVisitor, have it visit * the content of an {@code UnextractedMessage} (which will cause to to * accumulate all of the information it needs) and then call the * {@code getResult} method, which will return an {@code ExtractedMessage}, * or a stand-in if there was an error. */ private static class InsideMessageVisitor extends DefaultingExpressionVisitor { private final AlertSink alertSink; private final OutsideMessageVisitor outsideMessageVisitor; private final UnextractedMessage msg; // Mutable state: private final MessageBuilder tcMessageBuilder = new MessageBuilder(); private final List parameters = Lists.newArrayList(); private boolean invalid = false; /** * {@link com.google.i18n.Message} only supports the pattern "%[1-9%]" when * looking for dynamic placeholders, so only 9 dynamic placeholders are * allowed. */ private static final int MAX_DYNAMIC_PLACEHOLDER_COUNT = 9; InsideMessageVisitor(AlertSink alertSink, OutsideMessageVisitor outsideMessageVisitor, UnextractedMessage msg) { this.alertSink = Preconditions.checkNotNull(alertSink); this.outsideMessageVisitor = Preconditions.checkNotNull(outsideMessageVisitor); this.msg = Preconditions.checkNotNull(msg); // Populate the tcMessageBuilder with a content-type tcMessageBuilder.setContentType(msg.getSchema().getCanonicalContentType()); // Populate tcMessageBuilder with attributes from msg. // - The meaning is used to compute the msg id. if (null != msg.getMeaning()) { tcMessageBuilder.setMeaning(msg.getMeaning()); } // mark this message as hidden if indicated if (msg.isHidden()) { tcMessageBuilder.setHidden(true); } // - Comments are notes for translators if (null != msg.getComment()) { tcMessageBuilder.setDescription(msg.getComment()); } // - Include the source gxp file so that l10n experts for other projects // can avoid clobbering this project's translated strings. SourcePosition source = msg.getSourcePosition(); if (source != null) { tcMessageBuilder.addSource(source.getSource().toRelativeFilename() + ": L" + source.getLine() // For backwards compatibility with the old // compiler, report a 0-based column number. + ", C" + (source.getColumn() - 1)); } } Expression getResult() { if (!invalid) { try { Message message = tcMessageBuilder.createMessage(); ExtractedMessage emsg = new ExtractedMessage(msg, msg.getSchema(), msg.getName(), message, parameters); outsideMessageVisitor.addMessages(emsg); return emsg; } catch (InvalidMessageException imx) { recordInvalidMessageException(msg, imx); } } if (!invalid) { throw new AssertionError("Attempting to create stand-in when message is valid!"); } // TODO(laurence): create a better stand-in return new StringConstant(msg, msg.getSchema(), ""); } private void recordInvalidMessageException(Node node, InvalidMessageException imx) { alertSink.add(new InvalidMessageError(node.getSourcePosition(), imx)); invalid = true; } @Override public Void visitStringConstant(StringConstant value) { tcMessageBuilder.appendText(value.evaluate()); return null; } @Override public Void visitConcatenation(Concatenation value) { for (Expression subExpression : value.getValues()) { subExpression.acceptVisitor(this); } return null; } @Override public Void visitConvertibleToContent(ConvertibleToContent value) { value.getSubexpression().acceptVisitor(this); return null; } @Override public Void visitExampleExpression(ExampleExpression value) { return value.getSubexpression().acceptVisitor(this); } @Override protected Void defaultVisitExpression(Expression node) { alertSink.add(new BadNodePlacementError(node, msg)); return null; } @Override public Void visitPlaceholderNode(PlaceholderNode ph) { StringBuilder sb = new StringBuilder(); for (Expression subExpression : ph.getContent().separate()) { subExpression = subExpression.acceptVisitor(outsideMessageVisitor); if (subExpression.hasStaticString()) { String s = Preconditions.checkNotNull(subExpression.getStaticString(alertSink, null)); sb.append(s.replace("%", "%%")); } else { int index; for (index = 0; index < parameters.size(); index++) { if (parameters.get(index).alwaysEquals(subExpression)) { break; } } // Placeholders parameters start at 1. int id = index + 1; if (id > MAX_DYNAMIC_PLACEHOLDER_COUNT) { alertSink.add(new TooManyDynamicPlaceholdersError(subExpression)); } else { if (index == parameters.size()) { parameters.add(subExpression); } sb.append("%"); sb.append(String.valueOf(id)); } } } try { tcMessageBuilder.appendPlaceholder(sb.toString(), ph.getName().toUpperCase(), ph.getExample()); } catch (InvalidMessageException imx) { recordInvalidMessageException(ph, imx); } return null; } @Override public Void visitOutputElement(OutputElement value) { throw new UnexpectedNodeException(value); } @Override public Void visitCollapseExpression(CollapseExpression value) { throw new UnexpectedNodeException(value); } @Override public Void visitExtractedMessage(ExtractedMessage value) { throw new UnexpectedNodeException(value); } @Override public Void visitPlaceholderStart(PlaceholderStart value) { throw new UnexpectedNodeException(value); } @Override public Void visitPlaceholderEnd(PlaceholderEnd value) { throw new UnexpectedNodeException(value); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy