public final void testBadCall() throws Exception {
   runTest(
       "" + "<ihtml:template formals=\"\" name=\"hi\">\n" + "  \n" + "</ihtml:template>",
       ""
           + "<ihtml:template name='hi' formals=''>\n"
           + "  <ihtml:call foo='bar' baz:boo='far'/>\n"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.MISSING_ATTRIB,
           FilePosition.instance(is, 2, 41, 3, 37),
           elKey("ihtml:call"),
           attrKey("ihtml:call", "ihtml:template")),
       new Message(
           IhtmlMessageType.BAD_ATTRIB,
           FilePosition.instance(is, 2, 63, 25, 13),
           elKey("ihtml:call"),
           AttribKey.forAttribute(
               new Namespaces(Namespaces.XML_SPECIAL, "baz", "unknown:///baz"),
               elKey("ihtml:call"),
               "baz:boo"),
           MessagePart.Factory.valueOf("far")),
       new Message(
           MessageType.NO_SUCH_NAMESPACE,
           FilePosition.fromLinePositions(is, 2, 25, 2, 32),
           MessagePart.Factory.valueOf("baz"),
           MessagePart.Factory.valueOf("baz:boo")));
 }
 public final void testMisplacedPlaceholderContent() throws Exception {
   runTest(
       ""
           + "<ihtml:template formals=\"\" name=\"t\">\n"
           + "  <ihtml:message name=\"m\">\n"
           + "    \n"
           + "    \n"
           + "  </ihtml:message>\n"
           + "</ihtml:template>",
       ""
           + "<ihtml:template formals=\"\" name=\"t\">\n"
           + "  <ihtml:message name=\"m\">\n"
           + "    <ihtml:ph name=\"p\">Hi</ihtml:ph>\n"
           + "    <ihtml:eph>There</ihtml:eph>\n"
           + "  </ihtml:message>\n"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.INAPPROPRIATE_CONTENT,
           FilePosition.instance(is, 3, 88, 24, 2),
           MessagePart.Factory.valueOf("ph")),
       new Message(
           IhtmlMessageType.INAPPROPRIATE_CONTENT,
           FilePosition.instance(is, 4, 117, 16, 5),
           MessagePart.Factory.valueOf("eph")));
 }
 public final void testNestedMessage() throws Exception {
   runTest(
       ""
           + "<ihtml:template formals=\"\" name=\"t\">"
           + "<ihtml:message name=\"hi\">"
           + "Hello "
           + "<ihtml:ph name=\"planet\"></ihtml:ph>"
           + "<ihtml:dynamic expr=\"planet\"></ihtml:dynamic>"
           + "<ihtml:eph></ihtml:eph>!"
           + "</ihtml:message>"
           + "</ihtml:template>",
       ""
           + "<ihtml:template formals=\"\" name=\"t\">"
           + "<ihtml:message name=\"hi\">"
           + "<ihtml:message name=\"there\" />"
           + "Hello "
           + "<ihtml:ph name=\"planet\"/>"
           + "<ihtml:dynamic expr=\"planet\"/>"
           + "<ihtml:eph/>!"
           + "</ihtml:message>"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.NESTED_MESSAGE,
           FilePosition.instance(is, 1, 62, 62, 30),
           FilePosition.instance(is, 1, 37, 37, 145)));
 }
 public final void testTemplateNames() throws Exception {
   runTest(
       "<ihtml:template formals=\"x\" name=\"hi\"></ihtml:template>",
       ""
           + "<ihtml:template formals='x' name='hi'>"
           + "<ihtml:template name='3nested' formals='a,,x,if,3' zoinks='ahoy'/>"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.BAD_ATTRIB,
           FilePosition.instance(is, 1, 55, 55, 14),
           elKey("ihtml:template"),
           attrKey("ihtml:template", "name"),
           MessagePart.Factory.valueOf("3nested")),
       new Message(
           IhtmlMessageType.BAD_ATTRIB,
           FilePosition.instance(is, 1, 70, 70, 19),
           elKey("ihtml:template"),
           attrKey("ihtml:template", "formals"),
           MessagePart.Factory.valueOf("a,,x,if,3")),
       new Message(
           IhtmlMessageType.BAD_ATTRIB,
           FilePosition.instance(is, 1, 90, 90, 13),
           elKey("ihtml:template"),
           attrKey("ihtml:template", "zoinks"),
           MessagePart.Factory.valueOf("ahoy")));
 }
 public final void testUnnamedPlaceholder() throws Exception {
   runTest(
       ""
           + "<ihtml:template formals=\"\" name=\"t\">"
           + "<ihtml:message name=\"hi\">"
           + "Hello "
           + "<ihtml:dynamic expr=\"planet\"></ihtml:dynamic>"
           + "!"
           + "</ihtml:message>"
           + "</ihtml:template>",
       ""
           + "<ihtml:template formals=\"\" name=\"t\">"
           + "<ihtml:message name=\"hi\">"
           + "Hello "
           + "<ihtml:ph/>"
           + "<ihtml:dynamic expr=\"planet\"/>"
           + "<ihtml:eph/>!"
           + "</ihtml:message>"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.MISSING_ATTRIB,
           elKey("ihtml:ph"),
           attrKey("ihtml:ph", "name"),
           FilePosition.instance(is, 1, 68, 68, 11)),
       new Message(
           IhtmlMessageType.ORPHANED_PLACEHOLDER_END, FilePosition.instance(is, 1, 109, 109, 12)));
 }
 public final void testBadPlaceholderName() throws Exception {
   runTest(
       ""
           + "<ihtml:template formals=\"\" name=\"t\">"
           + "<ihtml:message name=\"hi\">"
           + "Hello "
           + "<ihtml:dynamic expr=\"planet\"></ihtml:dynamic>"
           + "!"
           + "</ihtml:message>"
           + "</ihtml:template>",
       ""
           + "<ihtml:template formals=\"\" name=\"t\">"
           + "<ihtml:message name=\"hi\">"
           + "Hello "
           + "<ihtml:ph name=\"if\"/>"
           + "<ihtml:dynamic expr=\"planet\"/>"
           + "<ihtml:eph/>!"
           + "</ihtml:message>"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.BAD_ATTRIB,
           FilePosition.instance(is, 1, 78, 78, 9),
           elKey("ihtml:ph"),
           attrKey("ihtml:ph", "name"),
           MessagePart.Factory.valueOf("if")),
       new Message(
           IhtmlMessageType.ORPHANED_PLACEHOLDER_END, FilePosition.instance(is, 1, 119, 119, 12)));
 }
 public final void testDynamicAttr() throws Exception {
   runTest(
       ""
           + "<ihtml:template formals=\"\" name=\"t\">\n"
           + "  <a>\n"
           + "    <ihtml:attribute name=\"href\">"
           + "<ihtml:dynamic expr=\"url\"></ihtml:dynamic>"
           + "</ihtml:attribute>\n"
           + "    <ihtml:attribute name=\"title\">\n"
           + "      <ihtml:message name=\"linkHover\">Howdy</ihtml:message>\n"
           + "    </ihtml:attribute>\n"
           + "    Link Text\n"
           + "    \n"
           + "  </a>\n"
           + "</ihtml:template>",
       ""
           + "<ihtml:template formals=\"\" name=\"t\">\n"
           + "  <a>\n"
           + "    <ihtml:attribute name=\"href\"><ihtml:dynamic expr=\"url\""
           + "     /></ihtml:attribute>\n"
           + "    <ihtml:attribute name=\"title\">\n"
           + "      <ihtml:message name=\"linkHover\">Howdy</ihtml:message>\n"
           + "    </ihtml:attribute>\n"
           + "    Link Text\n"
           + "    <ihtml:attribute>onclick=\"badness()\"</ihtml:attribute>\n"
           + "  </a>\n"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.MISSING_ATTRIB,
           FilePosition.instance(is, 8, 264, 5, 54),
           elKey("ihtml:attribute"),
           attrKey("ihtml:attribute", "name")));
 }
 public final void testUnclosedPlaceholder2() throws Exception {
   runTest(
       ""
           + "<ihtml:template formals=\"\" name=\"t\">"
           + "<ihtml:message name=\"hi\">"
           + "Hello "
           + "<ihtml:dynamic expr=\"planet\"></ihtml:dynamic>"
           + "<ihtml:ph name=\"punc\"></ihtml:ph>"
           + "!"
           + "<ihtml:eph></ihtml:eph>"
           + "</ihtml:message>"
           + "</ihtml:template>",
       ""
           + "<ihtml:template formals=\"\" name=\"t\">"
           + "<ihtml:message name=\"hi\">"
           + "Hello "
           + "<ihtml:ph name=\"planet\"/>"
           + "<ihtml:dynamic expr=\"planet\"/>"
           + "<ihtml:ph name=\"punc\"/>"
           + "!"
           + "<ihtml:eph/>"
           + "</ihtml:message>"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.UNCLOSED_PLACEHOLDER,
           // Ends before placeholder punc.
           FilePosition.instance(is, 1, 68, 68, 55)));
 }
 /**
  * Reduces the pending tokens to a single token with the given type. For example, if the pending
  * list contains an identifier followed by an open parenthesis, then it can be reduced to a single
  * function token. This is necessitated by CSS2's odd lexical convention which classifies as
  * single tokens things that most other languages treat as sequences of primitive tokens.
  *
  * <p>Modifies the pending list in place.
  */
 private void reduce(CssTokenType type) {
   StringBuilder sb = new StringBuilder();
   for (Token<CssTokenType> t : pending) {
     sb.append(t.text);
   }
   FilePosition fp = FilePosition.span(pending.getFirst().pos, pending.getLast().pos);
   pending.clear();
   pending.add(Token.instance(sb.toString(), type, fp));
 }
 public final void testMisplacedElementAndAttribute() throws Exception {
   runTest(
       ""
           + "<ihtml:template formals=\"\" name=\"t\">\n"
           + "  <ihtml:element></ihtml:element>\n"
           + "  <ihtml:message name=\"m\">\n"
           + "    \n"
           + "    <ihtml:ph name=\"ph\"></ihtml:ph>\n"
           + "      \n"
           + "    <ihtml:eph></ihtml:eph>\n"
           + "  </ihtml:message>\n"
           + "  <ihtml:do init=\"maybe\">\n"
           + "    <ihtml:element></ihtml:element>\n"
           + "  <ihtml:else></ihtml:else>\n"
           + "    <ihtml:element></ihtml:element>\n"
           + "  </ihtml:do>\n"
           + "</ihtml:template>",
       ""
           + "<ihtml:template formals=\"\" name=\"t\">\n"
           + "  <ihtml:element/>\n" // OK inside templates
           + "  <ihtml:message name=\"m\">\n"
           + "    <ihtml:element/>\n" // But not inside messages
           + "    <ihtml:ph name=\"ph\"/>\n"
           + "      <ihtml:element/>\n" // Not even inside messages
           + "    <ihtml:eph/>\n"
           + "  </ihtml:message>\n"
           + "  <ihtml:do init=\"maybe\">\n"
           + "    <ihtml:element/>\n" // OK inside a conditional
           + "  <ihtml:else/>\n"
           + "    <ihtml:element/>\n" // OK inside a conditional's alternate
           + "  </ihtml:do>\n"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.MISPLACED_ELEMENT,
           FilePosition.instance(is, 4, 88, 5, 16),
           MessagePart.Factory.valueOf("ihtml:element"),
           MessagePart.Factory.valueOf("ihtml:message")),
       new Message(
           IhtmlMessageType.MISPLACED_ELEMENT,
           FilePosition.instance(is, 6, 137, 7, 16),
           MessagePart.Factory.valueOf("ihtml:element"),
           MessagePart.Factory.valueOf("ihtml:message")));
 }
Exemple #11
0
 public static FilePosition span(FilePosition start, FilePosition end) {
   if (start == end) {
     return start;
   }
   if (!start.source.equals(end.source)) {
     return FilePosition.UNKNOWN;
   }
   if (start.startCharInFile > end.endCharInFile) {
     throw new IllegalArgumentException(start + ", " + end);
   }
   return instance(
       start.source,
       start.startLineNo,
       start.startCharInFile,
       start.startCharInLine,
       end.endLineNo(),
       end.endCharInFile(),
       end.endCharInLine());
 }
 public final void testOrphanedPlaceholder() throws Exception {
   runTest(
       ""
           + "<ihtml:template formals=\"\" name=\"t\">"
           + "Hello "
           + "<ihtml:dynamic expr=\"planet\"></ihtml:dynamic>!"
           + "</ihtml:template>",
       ""
           + "<ihtml:template formals=\"\" name=\"t\">"
           + "Hello "
           + "<ihtml:ph name=\"planet\"/>"
           + "<ihtml:dynamic expr=\"planet\"/>!"
           + "<ihtml:eph/>"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.ORPHANED_PLACEHOLDER, FilePosition.instance(is, 1, 43, 43, 25)),
       new Message(
           IhtmlMessageType.ORPHANED_PLACEHOLDER, FilePosition.instance(is, 1, 99, 99, 12)));
 }
Exemple #13
0
 public static FilePosition between(FilePosition a, FilePosition b) {
   return instance(
       a.source(),
       a.endLineNo(),
       a.endCharInFile(),
       a.endCharInLine(),
       b.startLineNo(),
       b.startCharInFile(),
       b.startCharInLine());
 }
 public final void testUnnamedMessage() throws Exception {
   runTest(
       "<ihtml:template formals=\"a b\" name=\"t\"></ihtml:template>",
       ""
           + "<ihtml:template formals='a b' name='t'>"
           + "<ihtml:message>Hello</ihtml:message>"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.MISSING_ATTRIB,
           elKey("ihtml:message"),
           attrKey("ihtml:message", "name"),
           FilePosition.instance(is, 1, 40, 40, 36)));
 }
 public final void testIhtmlElementInMessageOutsidePlaceholder() throws Exception {
   runTest(
       "" + "<ihtml:template formals=\"\" name=\"t\"></ihtml:template>",
       ""
           + "<ihtml:template formals=\"\" name=\"t\">"
           + "<ihtml:message name='SayHowdy'>"
           + "Hello "
           + "<ihtml:dynamic expr=\"planet\"/>!"
           + "</ihtml:message>"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.IHTML_IN_MESSAGE_OUTSIDE_PLACEHOLDER,
           FilePosition.instance(is, 1, 74, 74, 30),
           MessagePart.Factory.valueOf("dynamic")));
 }
 public final void testMisnamedMessage() throws Exception {
   runTest(
       "<ihtml:template formals=\"a b\" name=\"t\"></ihtml:template>",
       ""
           + "<ihtml:template formals=\"a b\" name=\"t\">"
           + "<ihtml:message name=\"x__\">"
           + "Hello"
           + "</ihtml:message>"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.BAD_ATTRIB,
           FilePosition.instance(is, 1, 55, 55, 10),
           elKey("ihtml:message"),
           attrKey("ihtml:message", "name"),
           MessagePart.Factory.valueOf("x__")));
 }
 public final void testBadAttr() throws Exception {
   runTest(
       ""
           + "<ihtml:template formals=\"\" name=\"t\">\n"
           + "  \n"
           + "  <div><ihtml:element>p</ihtml:element></div>\n"
           + "</ihtml:template>",
       ""
           + "<ihtml:template formals=\"\" name=\"t\">\n"
           + "  <ihtml:element bogus=\"\">p</ihtml:element>\n"
           + "  <div><ihtml:element>p</ihtml:element></div>\n"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.BAD_ATTRIB,
           FilePosition.instance(is, 2, 55, 18, 8),
           elKey("ihtml:element"),
           attrKey("ihtml:element", "bogus"),
           MessagePart.Factory.valueOf("")));
 }
 public final void testContentType() throws Exception {
   // The callingContext attribute is set by a later pass, so make sure it
   // can't be passed in.
   runTest(
       "" + "<ihtml:template formals=\"\" name=\"main\">\n" + "  \n" + "</ihtml:template>",
       ""
           + "<ihtml:template formals=\"\" name=\"main\">\n"
           + "  <ihtml:template formals=\"\" name=\"sub\"\n"
           + "   "
           + IHTML.CALLING_CONTEXT_ATTR
           + "=\"div\">\n"
           + "  </ihtml:template>\n"
           + "</ihtml:template>",
       new Message(
           IhtmlMessageType.BAD_ATTRIB,
           FilePosition.instance(is, 3, 4, 4, 20),
           elKey("ihtml:template"),
           attrKey("ihtml:template", "callingContext"),
           MessagePart.Factory.valueOf("div")));
 }
  private void runTest(String goldenIhtml, String inputIhtml, Message... expectedMessages)
      throws Exception {
    Element ihtmlRoot =
        new DomParser(
                DomParser.makeTokenQueue(
                    FilePosition.startOfFile(is), new StringReader(inputIhtml), true, false),
                true,
                mq)
            .parseDocument();
    new IhtmlSanityChecker(mq).check(ihtmlRoot);

    for (Message msg : expectedMessages) {
      assertMessage(
          true,
          msg.getMessageType(),
          msg.getMessageLevel(),
          msg.getMessageParts().toArray(new MessagePart[0]));
    }
    assertMessagesLessSevereThan(MessageLevel.WARNING);

    String checkedIhtml = Nodes.render(ihtmlRoot, MarkupRenderMode.XML);
    assertEquals(goldenIhtml, checkedIhtml);
  }
Exemple #20
0
  void processToken(String text) {
    TokenClassification tClass = TokenClassification.classify(text);
    if (tClass == null) {
      return;
    }
    switch (tClass) {
      case LINEBREAK:
        // Allow external code to force line-breaks.
        // This allows us to create a composite-renderer that renders
        // original source code next to translated source code.
        emit("\n");
        return;
      case SPACE:
        pendingSpace = true;
        return;
      case COMMENT:
        if (mark != null && lastLine != mark.startLineNo()) {
          newline();
          lastLine = mark.startLineNo();
        } else if ("/".equals(lastToken) || pendingSpace) {
          space();
        }
        pendingSpace = false;
        emit(text);
        if (text.startsWith("//")) {
          newline();
          pendingSpace = false;
        } else {
          pendingSpace = true;
        }
        return;
      default:
        break;
    }

    boolean spaceBefore = pendingSpace;
    pendingSpace = false;
    boolean spaceAfter = false;

    // Determine which pairs of tokens cannot be adjacent and put a space
    // between them.
    if (tClass == lastClass) {
      // Adjacent punctuation, strings, and words require space.
      // Numbers and words are both of type OTHER.
      // This decision may be revisited in the following to prevent
      // excessive space inside parentheses.
      spaceBefore = !"(".equals(lastToken);
    } else if (lastClass == TokenClassification.REGEX) {
      if (tClass == TokenClassification.OTHER || "/".equals(text)) {
        // Make sure words don't run into regex flags, and that / operator
        // does not combine with end of regex to make a line comment.
        spaceBefore = true;
      }
    } else if (tClass == TokenClassification.REGEX && "/".equals(lastToken)) {
      // Allowing these two tokens to run together could introduce a line
      // comment.
      spaceBefore = true;
    } else if (tClass == TokenClassification.OTHER
        && Character.isDigit(text.charAt(0))
        && ".".equals(lastToken)) {
      // Following a dot operator with a number is illegal syntactically, but
      // this renderer should not allow any lexical confusion.
      spaceBefore = true;
    }

    if (tClass == TokenClassification.OTHER) {
      if ("}".equals(lastToken)) {
        spaceBefore = true;
      }
      if (isKeyword(text.toString())) {
        // Put a space between if and other keywords and the parenthesis.
        spaceAfter = true;
      }
    }

    // If this token is an open bracket, we want to indent, but not before
    // writing the token to avoid over-indenting the open bracket.
    if (text.length() == 1) {
      char ch0 = text.charAt(0);
      switch (ch0) {
        case '{':
          if (lastClass == TokenClassification.PUNCTUATION) {
            if (":".equals(lastToken)) { // See JSON test.
              spaceBefore = true;
            } else if (!(")".equals(lastToken) || "=".equals(lastToken))) {
              // If starting a block following a parenthesized condition, or
              // an object literal assigned.
              spaceBefore = !("(".equals(lastToken) || "[".equals(lastToken));
            }
          }
          spaceAfter = true;
          break;
        case '[':
          if (")".equals(lastToken)) {
            spaceBefore = false;
          }
          spaceAfter = true;
          break;
        case '(':
          if (")".equals(lastToken)) { // Calling a parenthesized value.
            spaceBefore = false;
          }
          break;
        case '}':
          spaceBefore = !"{".equals(lastToken);
          spaceAfter = true;
          break;
        case ')':
          spaceBefore = false;
          spaceAfter = true;
          break;
        case ']':
          spaceBefore = !"}".equals(lastToken);
          spaceAfter = true;
          break;
        case ',':
          spaceBefore = false;
          spaceAfter = true;
          break;
        case ';':
          spaceBefore = false;
          spaceAfter = true;
          break;
        case ':':
          spaceBefore = ":".equals(lastToken); // Since :: is a token in ES4
          spaceAfter = true;
          break;
        case '=':
          spaceBefore = true;
          spaceAfter = true;
          break;
        case '.':
          spaceBefore =
              lastToken != null
                  && (TokenClassification.isNumber(lastToken) || ".".equals(lastToken));
          spaceAfter = false;
          break;
      }
    }

    // Write any whitespace before the token.
    if (spaceBefore) {
      space();
    }

    // Actually write the token.
    emit(text);

    pendingSpace = spaceAfter;

    lastClass = tClass;
    lastToken = text;
    if (mark != null) {
      lastLine = mark.startLineNo();
    }
  }
Exemple #21
0
 public static FilePosition endOf(FilePosition fp) {
   return FilePosition.instance(fp.source, fp.endLineNo, fp.endCharInFile, fp.endCharInLine);
 }
Exemple #22
0
 public static FilePosition startOf(FilePosition fp) {
   return FilePosition.instance(fp.source, fp.startLineNo, fp.startCharInFile, fp.startCharInLine);
 }
 public final void mark(@Nullable FilePosition mark) {
   if (mark != null && !InputSource.UNKNOWN.equals(mark.source())) {
     pending.add(mark);
   }
 }