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

hudson.util.TableNestChecker Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 *
 * Copyright (c) 2004-2009 Oracle Corporation.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * 
 *    Kohsuke Kawaguchi
 *
 *
 *******************************************************************************/ 

package hudson.util;

import org.xml.sax.helpers.XMLFilterImpl;
import org.xml.sax.XMLFilter;
import org.xml.sax.ContentHandler;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.apache.commons.jelly.XMLOutput;

import java.util.Locale;
import java.util.Stack;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.Arrays;

/**
 * {@link XMLFilter} that checks the proper nesting of table related tags.
 *
 * 

Browser often "fixes" HTML by moving tables into the right place, so * failure to generate proper tables can result in a hard-to-track bugs. * *

TODO: where to apply this in stapler? JellyClassTearOff creates * XMLOutput. Perhaps we define a decorator? We can also wrap Script. would that * work better? * * @author Kohsuke Kawaguchi */ public class TableNestChecker extends XMLFilterImpl { private final Stack elements = new Stack(); private final Stack tagNames = new Stack(); public static void applyTo(XMLOutput xo) { xo.setContentHandler(new TableNestChecker(xo.getContentHandler())); } public TableNestChecker() { elements.push(ALL_ALLOWED); } public TableNestChecker(ContentHandler target) { this(); setContentHandler(target); } @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { String tagName = localName.toUpperCase(Locale.ENGLISH); // make sure that this tag occurs in the proper context if (!elements.peek().isAllowed(tagName)) { throw new SAXException(tagName + " is not allowed inside " + tagNames.peek()); } Checker next = CHECKERS.get(tagName); if (next == null) { next = ALL_ALLOWED; } elements.push(next); tagNames.push(tagName); super.startElement(uri, localName, qName, atts); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { elements.pop(); tagNames.pop(); super.endElement(uri, localName, qName); } private interface Checker { boolean isAllowed(String childTag); } private static final Checker ALL_ALLOWED = new Checker() { public boolean isAllowed(String childTag) { return true; } }; private static final class InList implements Checker { private final Set tags; private InList(String... tags) { this.tags = new HashSet(Arrays.asList(tags)); } public boolean isAllowed(String childTag) { return tags.contains(childTag); } } private static final Map CHECKERS = new HashMap(); static { CHECKERS.put("TABLE", new InList("TR", "THEAD", "TBODY")); InList rows = new InList("TR"); CHECKERS.put("THEAD", rows); CHECKERS.put("THEAD", rows); CHECKERS.put("TR", new InList("TD", "TH")); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy