/** Dumps messages to the given output stream, returning the highest message level seen. */ static MessageLevel dumpMessages(MessageQueue mq, MessageContext mc, Appendable out) { MessageLevel maxLevel = MessageLevel.values()[0]; for (Message m : mq.getMessages()) { MessageLevel level = m.getMessageLevel(); if (maxLevel.compareTo(level) < 0) { maxLevel = level; } } MessageLevel ignoreLevel = null; if (maxLevel.compareTo(MessageLevel.LINT) < 0) { // If there's only checkpoints, be quiet. ignoreLevel = MessageLevel.LOG; } try { for (Message m : mq.getMessages()) { MessageLevel level = m.getMessageLevel(); if (ignoreLevel != null && level.compareTo(ignoreLevel) <= 0) { continue; } out.append(level.name() + ": "); m.format(mc, out); out.append("\n"); if (maxLevel.compareTo(level) < 0) { maxLevel = level; } } } catch (IOException ex) { ex.printStackTrace(); } return maxLevel; }
/** * @param containingHtmlElement the name of the HTML element containing el. If the HTML element is * contained inside a template construct then this name may differ from el's immediate parent. */ private void inspectElement(JobEnvelope source, Element el, ElKey containingHtmlElement) { ElKey elKey = ElKey.forElement(el); // Recurse early so that ihtml:dynamic elements have been parsed before we // process the attributes element list. for (Node child : Nodes.childrenOf(el)) { inspect(source, child, elKey); } // For each attribute allowed on this element type, ensure that // (1) If it is not specified, and its default value is not allowed, then // it is added with a known safe value. // (2) Its value is rewritten as appropriate. // We don't have to worry about disallowed attributes since those will // not be present in scriptsPerNode. The TemplateSanitizer should have // stripped those out. The TemplateSanitizer should also have stripped out // disallowed elements. if (!htmlSchema.isElementAllowed(elKey)) { return; } HTML.Element elInfo = htmlSchema.lookupElement(elKey); List<HTML.Attribute> attrs = elInfo.getAttributes(); if (attrs != null) { for (HTML.Attribute a : attrs) { AttribKey attrKey = a.getKey(); if (!htmlSchema.isAttributeAllowed(attrKey)) { continue; } Attr attr = null; String aUri = attrKey.ns.uri; String aName = attrKey.localName; Attr unsafe = el.getAttributeNodeNS(aUri, aName); if (unsafe != null && a.getValueCriterion().accept(unsafe.getValue())) { attr = unsafe; } else if ((a.getDefaultValue() != null && !a.getValueCriterion().accept(a.getDefaultValue())) || !a.isOptional()) { attr = el.getOwnerDocument().createAttributeNS(aUri, aName); String safeValue; if (a.getType() == HTML.Attribute.Type.URI) { safeValue = "" + Nodes.getFilePositionFor(el).source().getUri(); } else { safeValue = a.getSafeValue(); } if (safeValue == null) { mq.addMessage( IhtmlMessageType.MISSING_ATTRIB, Nodes.getFilePositionFor(el), elKey, attrKey); continue; } attr.setNodeValue(safeValue); el.setAttributeNodeNS(attr); } if (attr != null) { inspectHtmlAttribute(source, attr, a); } } } scriptsPerNode.put(el, null); }
/** * Examines the HTML document and writes messages about problematic portions to the message queue * passed to the constructor. */ private void inspect() { if (!mq.hasMessageAtLevel(MessageLevel.FATAL_ERROR)) { for (IhtmlRoot ihtmlRoot : ihtmlRoots) { HtmlEmbeddedContentFinder finder = new HtmlEmbeddedContentFinder(htmlSchema, ihtmlRoot.baseUri, mq, mc); for (EmbeddedContent c : finder.findEmbeddedContent(ihtmlRoot.root)) { Node src = c.getSource(); if (src instanceof Attr) { embeddedContent.put((Attr) src, c); } } inspect(ihtmlRoot.source, ihtmlRoot.root, ElKey.forHtmlElement("div")); } } }
private boolean parseInputs(Collection<URI> inputs, PluginCompiler pluginc) { boolean parsePassed = true; for (URI input : inputs) { try { ParseTreeNode parseTree = parseInput(input); if (null != parseTree) { pluginc.addInput(new AncestorChain<ParseTreeNode>(parseTree)); } } catch (ParseException ex) { ex.toMessageQueue(mq); parsePassed = false; } catch (IOException ex) { mq.addMessage(MessageType.IO_ERROR, MessagePart.Factory.valueOf(ex.toString())); parsePassed = false; } } return parsePassed; }
private void produce() throws ParseException { if (null != pending) { return; } if (cp.isEmpty()) { return; } char[] buf = cp.getBuffer(); final int start = cp.getOffset(); int limit = cp.getLimit(); int end = start + 1; CssTokenType type; char ch = buf[start]; int identEnd; if (CssLexer.isSpaceChar(ch)) { // [ \t\r\n\f]+ S end = parseWhitespace(buf, end, limit); type = CssTokenType.SPACE; } else if (ch == '/') { if (end < limit && buf[end] == '*') { // \/\*[^*]*\*+([^/*][^*]*\*+)*\/ /* ignore comments */ int state = 0; // 0 - start, 1 - in comment, 2 - saw, 3 - done do { if (end == limit) { break; } ch = buf[end]; switch (state) { case 0: state = 1; break; case 1: if (ch == '*') { state = 2; } break; case 2: if (ch == '/') { state = 3; } else if (ch != '*') { state = 1; } break; } ++end; } while (state != 3); if (state != 3) { throw new ParseException( new Message( MessageType.UNTERMINATED_COMMENT_TOKEN, cp.filePositionForOffsets(start, end))); } type = CssTokenType.COMMENT; } else if (end < limit && buf[end] == '/') { do { if (++end == limit) { break; } ch = buf[end]; // Line comment does not contain the newline character that ends it // since we don't want to break \r\n sequences across two tokens, // and for consistency with JavaScript conventions which exclude the // newline from the line comment token. if (ch == '\r' || ch == '\n') { break; } } while (true); type = CssTokenType.COMMENT; FilePosition commentPos = cp.filePositionForOffsets(start, end); mq.addMessage(MessageType.INVALID_CSS_COMMENT, commentPos); } else { // *yytext type = CssTokenType.PUNCTUATION; } } else if ('~' == ch || '|' == ch) { if (end < limit && '=' == buf[end]) { // "~=" INCLUDES // "|=" DASHMATCH ++end; } else { // . *yytext } type = CssTokenType.PUNCTUATION; } else if (ch == '\'' || ch == '"') { end = parseString(cp, start); type = CssTokenType.STRING; } else if (ch == '@') { identEnd = parseIdent(cp, end); if (identEnd != -1) { // "@import" IMPORT_SYM // "@page" PAGE_SYM // "@media" MEDIA_SYM // "@font-face" FONT_FACE_SYM // "@charset " CHARSET_SYM // "@"{ident} ATKEYWORD type = CssTokenType.SYMBOL; end = identEnd; // In http://www.w3.org/TR/CSS21/grammar.html, the CHARSET_SYM is // allowed to match only "@charset " if ((end - start) == 8 && parseMatch(cp, start, "@charset ") > 0) { ++end; } } else { // . *yytext type = CssTokenType.PUNCTUATION; } } else if (ch == '!') { // "!{w}important" IMPORTANT_SYM // handled by token joining at a later pass // . *yytext type = CssTokenType.PUNCTUATION; } else if (ch == '#') { int nameEnd = parseName(cp, end); if (nameEnd >= 0) { // "#"{name} HASH type = CssTokenType.HASH; end = nameEnd; } else { // . *yytext type = CssTokenType.PUNCTUATION; } } else if (ch == '<' || ch == '-') { // "<!--" CDO // "-->" CDC int tailEnd = parseMatch(cp, end, ch == '<' ? "!--" : "->"); if (tailEnd >= 0) { end = tailEnd; } type = CssTokenType.PUNCTUATION; } else if ((ch >= '0' && ch <= '9') || '.' == ch) { // {num}em EMS // {num}ex EXS // {num}px LENGTH // {num}cm LENGTH // {num}mm LENGTH // {num}in LENGTH // {num}pt LENGTH // {num}pc LENGTH // {num}deg ANGLE // {num}rad ANGLE // {num}grad ANGLE // {num}ms TIME // {num}s TIME // {num}Hz FREQ // {num}kHz FREQ // {num}{ident} DIMEN // {num}% PERCENTAGE // {num} NUMBER boolean isNum; if ('.' == ch) { int numEnd = parseInt(cp, end); isNum = numEnd >= 0; if (isNum) { end = numEnd; } } else { isNum = true; end = parseNum(cp, start); } if (isNum) { identEnd = parseIdent(cp, end); if (identEnd >= 0) { end = identEnd; } else if (end < limit && '%' == buf[end]) { ++end; } type = CssTokenType.QUANTITY; } else { // lone . // . *yytext type = CssTokenType.PUNCTUATION; } } else if ((identEnd = parseIdent(cp, start)) >= 0) { end = identEnd; if (end - start == 1 && 'U' == ch && end < limit && '+' == buf[end]) { // U\+{range} UNICODERANGE // U\+{h}{1,6}-{h}{1,6} UNICODERANGE // range \?{1,6}|{h}(\?{0,5}|{h}(\?{0,4}|{h}\ // (\?{0,3}|{h}(\?{0,2}|{h}(\??|{h}))))) type = CssTokenType.UNICODE_RANGE; ++end; end = parseRange(cp, end); } else if (end < limit && '(' == buf[end]) { ++end; if (end - start == 4 && parseMatch(cp, start, "url(") >= 0) { // "url("{w}{string}{w}")" URI // "url("{w}{url}{w}")" URI end = parseWhitespace(buf, end, limit); int stringEnd = parseString(cp, end); int uriEnd = stringEnd < 0 ? parseUri(cp, end) : -1; if (stringEnd < 0 && uriEnd < 0) { throw new ParseException( new Message( MessageType.EXPECTED_TOKEN, cp.filePositionForOffsets(end, end), MessagePart.Factory.valueOf("{url}"), toMessagePart(cp, end))); } end = stringEnd >= 0 ? stringEnd : uriEnd; end = parseWhitespace(buf, end, limit); if (end == limit || ')' != buf[end]) { throw new ParseException( new Message( MessageType.EXPECTED_TOKEN, cp.filePositionForOffsets(end, end), MessagePart.Factory.valueOf(")"), toMessagePart(cp, end))); } ++end; type = CssTokenType.URI; } else { // {ident}"(" FUNCTION type = CssTokenType.FUNCTION; } } else { // {ident} IDENT type = CssTokenType.IDENT; } } else if (ch == '$' && allowSubstitutions) { // ${<javascript tokens>} if (end < limit && buf[end] != '{') { type = CssTokenType.PUNCTUATION; } else { // 0 - non string // 1 - quoted string // 2 - saw \ in string // 3 - saw close paren int state = 0; // number of parenthetical blocks entered and not exited int nOpen = 0; char delim = 0; do { if (end == limit) { break; } ch = buf[end]; switch (state) { case 0: if (ch == '"' || ch == '\'') { delim = ch; state = 1; } else if (ch == '{') { ++nOpen; } else if (ch == '}') { if (--nOpen == 0) { state = 3; } } break; case 1: if (ch == delim) { state = 0; } else if (ch == '\\') { state = 2; } break; case 2: state = 1; break; } ++end; } while (state != 3); if (state != 3) { throw new ParseException( new Message( MessageType.UNTERMINATED_STRING_TOKEN, cp.filePositionForOffsets(start, end))); } identEnd = parseIdent(cp, end); if (identEnd >= 0) { end = identEnd; } else if (end != limit && '%' == buf[end]) { ++end; } type = CssTokenType.SUBSTITUTION; } } else { // . *yytext type = CssTokenType.PUNCTUATION; } assert end > start; pending = Token.instance(cp.toString(start, end), type, cp.filePositionForOffsets(start, end)); cp.consumeTo(end); }