
2005-04-19 Andrew John Hughes <gnu_andrew@member.fsf.org> * gnu/xml/dom/html2/DomHTMLParser.java: Changed 'enum' references to become 'enumeration'. 2005-04-19 Audrius Meskauskas <audriusa@bluewin.ch> * gnu/xml/dom/html2/DomHTMLParser.java: New file. 2005-04-19 Andrew John Hughes <gnu_andrew@member.fsf.org> * gnu/javax/swing/text/html/parser/htmlAttributeSet.java: (getAttributeNames()): Replaced 'enum' with 'enumeration'. * gnu/javax/swing/text/html/parser/htmlValidator.java: (validateParameters(TagElement,htmlAttributeSet)): Replaced 'enum' with 'enumeration'. (validateAttribute(TagElement,htmlAttributeSet,Enumeration,Enumeration)): Likewise. 2005-04-19 Audrius Meskauskas <audriusa@bluewin.ch> * javax/swing/text/html/parser/Entity.java (getType): New method. * javax/swing/text/html/parser/DocumentParser.java: Inherit from javax.swing.text.html.parser.Parser. 2005-04-19 Chris Burdess <dog@gnu.org> * gnu/xml/dom/html2/DomHTMLDocument.java: Fixed element creation and check for HTML/XHTML namespace. 2005-04-19 Audrius Meskauskas <audriusa@bluewin.ch> * javax/swing/text/html/parser/Parser.java, javax/swing/text/html/parser/Entity.java: Inheriting from DTDConstants. * javax/swing/text/html/parser/AttributeList.java (getValues): Changed return type. * javax/swing/text/html/parser/DocumentParser (parse): Adding the callback parameter that receives the parsing events. 2005-04-19 Chris Burdess <dog@gnu.org> * gnu/xml/dom/DomImpl.java, gnu/xml/dom/html2/DomHTMLAnchorElement.java, gnu/xml/dom/html2/DomHTMLDocument.java, gnu/xml/dom/html2/DomHTMLElement.java, gnu/xml/dom/html2/DomHTMLFormElement.java, gnu/xml/dom/html2/DomHTMLFrameElement.java, gnu/xml/dom/html2/DomHTMLIFrameElement.java, gnu/xml/dom/html2/DomHTMLImpl.java, gnu/xml/dom/html2/DomHTMLInputElement.java, gnu/xml/dom/html2/DomHTMLObjectElement.java, gnu/xml/dom/html2/DomHTMLOptionElement.java, gnu/xml/dom/html2/DomHTMLSelectElement.java, gnu/xml/dom/html2/DomHTMLTableCellElement.java, gnu/xml/dom/html2/DomHTMLTableElement.java, gnu/xml/dom/html2/DomHTMLTableRowElement.java, gnu/xml/dom/html2/DomHTMLTableSectionElement.java, gnu/xml/dom/html2/DomHTMLTextAreaElement.java: JAXP integration, UI events, and tree utility functions. 2005-04-19 Michael Koch <konqueror@gmx.de> * gnu/javax/swing/text/html/parser/HTML_401F.java, gnu/javax/swing/text/html/parser/gnuDTD.java, gnu/javax/swing/text/html/parser/models/node.java: Reworked import statements. 2005-04-19 Audrius Meskauskas, Lithuania <AudriusA@Bioinformatics.org> * javax/swing/text/html/HTMLDocument.java: New file. 2005-04-19 Michael Koch <konqueror@gmx.de> * javax/swing/text/html/HTMLFrameHyperlinkEvent.java: Reformatted. * javax/swing/text/html/parser/AttributeList.java: Fixed order of modifiers. (AttributeList): Made final. * javax/swing/text/html/parser/ContentModel.java: Fixed html characters in javadocs. * javax/swing/text/html/parser/DTD.java (DTD): Don't implement java.io.Serializable directly. (getElement): Simplified. * javax/swing/text/html/parser/DTDConstants.java: Reformatted file. * javax/swing/text/html/parser/Element.java: Fixed order of modifiers. * javax/swing/text/html/parser/Parser.java: Reformatted. Don't use fully-qualified class names. * javax/swing/text/html/parser/ParserDelegator.java: Fixed order of modifiers. * javax/swing/text/rtf/RTFParser.java: Re-ordered import statements. * javax/swing/text/rtf/RTFScanner.java: Removed unused import statement. 2005-04-19 Chris Burdess <dog@gnu.org> * gnu/xml/dom/html2/DomHTMLButtonElement.java, gnu/xml/dom/html2/DomHTMLCollection.java, gnu/xml/dom/html2/DomHTMLDocument.java, gnu/xml/dom/html2/DomHTMLElement.java: Extensions for new element types. * gnu/xml/dom/html2/DomHTMLDListElement.java, gnu/xml/dom/html2/DomHTMLDirectoryElement.java, gnu/xml/dom/html2/DomHTMLDivElement.java, gnu/xml/dom/html2/DomHTMLFieldSetElement.java, gnu/xml/dom/html2/DomHTMLFontElement.java, gnu/xml/dom/html2/DomHTMLFormElement.java, gnu/xml/dom/html2/DomHTMLFrameElement.java, gnu/xml/dom/html2/DomHTMLFrameSetElement.java, gnu/xml/dom/html2/DomHTMLHRElement.java, gnu/xml/dom/html2/DomHTMLHeadElement.java, gnu/xml/dom/html2/DomHTMLHeadingElement.java, gnu/xml/dom/html2/DomHTMLHtmlElement.java, gnu/xml/dom/html2/DomHTMLIFrameElement.java, gnu/xml/dom/html2/DomHTMLImageElement.java, gnu/xml/dom/html2/DomHTMLInputElement.java, gnu/xml/dom/html2/DomHTMLIsIndexElement.java, gnu/xml/dom/html2/DomHTMLLIElement.java, gnu/xml/dom/html2/DomHTMLLabelElement.java, gnu/xml/dom/html2/DomHTMLLegendElement.java, gnu/xml/dom/html2/DomHTMLLinkElement.java, gnu/xml/dom/html2/DomHTMLMapElement.java, gnu/xml/dom/html2/DomHTMLMenuElement.java, gnu/xml/dom/html2/DomHTMLMetaElement.java, gnu/xml/dom/html2/DomHTMLModElement.java, gnu/xml/dom/html2/DomHTMLOListElement.java, gnu/xml/dom/html2/DomHTMLObjectElement.java, gnu/xml/dom/html2/DomHTMLOptGroupElement.java, gnu/xml/dom/html2/DomHTMLOptionElement.java, gnu/xml/dom/html2/DomHTMLParagraphElement.java, gnu/xml/dom/html2/DomHTMLParamElement.java, gnu/xml/dom/html2/DomHTMLPreElement.java, gnu/xml/dom/html2/DomHTMLQuoteElement.java, gnu/xml/dom/html2/DomHTMLScriptElement.java, gnu/xml/dom/html2/DomHTMLSelectElement.java, gnu/xml/dom/html2/DomHTMLStyleElement.java, gnu/xml/dom/html2/DomHTMLTableCaptionElement.java, gnu/xml/dom/html2/DomHTMLTableCellElement.java, gnu/xml/dom/html2/DomHTMLTableColElement.java, gnu/xml/dom/html2/DomHTMLTableElement.java, gnu/xml/dom/html2/DomHTMLTableRowElement.java, gnu/xml/dom/html2/DomHTMLTableSectionElement.java, gnu/xml/dom/html2/DomHTMLTextAreaElement.java, gnu/xml/dom/html2/DomHTMLTitleElement.java, gnu/xml/dom/html2/DomHTMLUListElement.java: New files. 2005-04-19 Audrius Meskauskas <audriusa@bluewin.ch> * javax/swing/text/ChangedCharSetException.java, javax/swing/text/html/HTMLEditorKit.java, javax/swing/text/html/HTMLFrameHyperlinkEvent.java, javax/swing/text/html/parser/AttributeList.java, javax/swing/text/html/parser/ContentModel.java, javax/swing/text/html/parser/DocumentParser.java, javax/swing/text/html/parser/DTD.java, javax/swing/text/html/parser/DTDConstants.java, javax/swing/text/html/parser/Element.java, javax/swing/text/html/parser/Entity.java, javax/swing/text/html/parser/Parser.java, javax/swing/text/html/parser/TagElement.java, gnu/javax/swing/text/html/package.html, gnu/javax/swing/text/html/parser/gnuDTD.java, gnu/javax/swing/text/html/parser/HTML_401F.java, gnu/javax/swing/text/html/parser/htmlAttributeSet.java, gnu/javax/swing/text/html/parser/htmlValidator.java, gnu/javax/swing/text/html/parser/package.html, gnu/javax/swing/text/html/parser/models/list.java, gnu/javax/swing/text/html/parser/models/node.java, gnu/javax/swing/text/html/parser/models/noTagModel.java, gnu/javax/swing/text/html/parser/models/package.html, gnu/javax/swing/text/html/parser/models/PCDATAonly_model.java, gnu/javax/swing/text/html/parser/models/TableRowContentModel.java, gnu/javax/swing/text/html/parser/models/transformer.java, gnu/javax/swing/text/html/parser/support/gnuStringIntMapper.java, gnu/javax/swing/text/html/parser/support/package.html, gnu/javax/swing/text/html/parser/support/parameterDefaulter.java, gnu/javax/swing/text/html/parser/support/Parser.java, gnu/javax/swing/text/html/parser/support/textPreProcessor.java, gnu/javax/swing/text/html/parser/support/low/Buffer.java, gnu/javax/swing/text/html/parser/support/low/Constants.java, gnu/javax/swing/text/html/parser/support/low/Location.java, gnu/javax/swing/text/html/parser/support/low/node.java, gnu/javax/swing/text/html/parser/support/low/package.html, gnu/javax/swing/text/html/parser/support/low/ParseException.java, gnu/javax/swing/text/html/parser/support/low/pattern.java, gnu/javax/swing/text/html/parser/support/low/Queue.java, gnu/javax/swing/text/html/parser/support/low/ReaderTokenizer.java: New files. * javax/swing/text/html/HTML.java, javax/swing/text/html/parser/ParserDelegator.java: New files (replacing). * javax/swing/text/html/package.html, javax/swing/text/html/parser/package.html: Documenting the packages. 2005-04-19 Chris Burdess <dog@gnu.org> * gnu/xml/dom/html2/DomHTMLAnchorElement.java, gnu/xml/dom/html2/DomHTMLAppletElement.java, gnu/xml/dom/html2/DomHTMLAreaElement.java, gnu/xml/dom/html2/DomHTMLBaseElement.java, gnu/xml/dom/html2/DomHTMLBaseFontElement.java, gnu/xml/dom/html2/DomHTMLBodyElement.java, gnu/xml/dom/html2/DomHTMLBRElement.java, gnu/xml/dom/html2/DomHTMLButtonElement.java, gnu/xml/dom/html2/DomHTMLCollection.java, gnu/xml/dom/html2/DomHTMLDocument.java, gnu/xml/dom/html2/DomHTMLElement.java: New files. 2005-04-19 Michael Koch <konqueror@gmx.de> * Makefile.am: Added new files. * Makefile.in: Regenerated. From-SVN: r98406
610 lines
19 KiB
Java
610 lines
19 KiB
Java
/* tagStack.java -- The HTML tag stack.
|
|
Copyright (C) 2005 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Classpath.
|
|
|
|
GNU Classpath is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
GNU Classpath is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU Classpath; see the file COPYING. If not, write to the
|
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307 USA.
|
|
|
|
Linking this library statically or dynamically with other modules is
|
|
making a combined work based on this library. Thus, the terms and
|
|
conditions of the GNU General Public License cover the whole
|
|
combination.
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent
|
|
modules, and to copy and distribute the resulting executable under
|
|
terms of your choice, provided that you also meet, for each linked
|
|
independent module, the terms and conditions of the license of that
|
|
module. An independent module is a module which is not derived from
|
|
or based on this library. If you modify this library, you may extend
|
|
this exception to your version of the library, but you are not
|
|
obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your version. */
|
|
|
|
|
|
package gnu.javax.swing.text.html.parser;
|
|
|
|
import gnu.javax.swing.text.html.parser.models.node;
|
|
import gnu.javax.swing.text.html.parser.models.transformer;
|
|
|
|
import java.util.BitSet;
|
|
import java.util.Enumeration;
|
|
import java.util.LinkedList;
|
|
import java.util.ListIterator;
|
|
|
|
import javax.swing.text.SimpleAttributeSet;
|
|
import javax.swing.text.html.HTML;
|
|
import javax.swing.text.html.parser.*;
|
|
|
|
/**
|
|
* <p>The HTML content validator, is responsible for opening and
|
|
* closing elements with optional start/end tags, detecting
|
|
* the wrongly placed html tags and reporting errors. The working instance
|
|
* is the inner class inside the {@link javax.swing.text.html.parser.Parser }
|
|
* </p>
|
|
* <p>This class could potentially
|
|
* provide basis for automated closing and insertion of the html tags,
|
|
* correcting the found html errors.
|
|
* @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
|
|
*/
|
|
public abstract class htmlValidator
|
|
{
|
|
/**
|
|
* The tag reference, holding additional information that the tag
|
|
* has been forcibly closed.
|
|
*/
|
|
protected class hTag
|
|
{
|
|
protected final Element element;
|
|
protected final HTML.Tag tag;
|
|
protected final TagElement tgElement;
|
|
protected boolean forcibly_closed;
|
|
protected node validationTrace;
|
|
|
|
protected hTag(TagElement an_element)
|
|
{
|
|
element = an_element.getElement();
|
|
tag = an_element.getHTMLTag();
|
|
tgElement = an_element;
|
|
|
|
if (element.content != null)
|
|
validationTrace = transformer.transform(element.content, dtd);
|
|
}
|
|
|
|
/**
|
|
* This is called when the tag must be forcibly closed because
|
|
* it would make the newly appearing tag invalid.
|
|
* The parser is not notified about such event (just the error
|
|
* is reported). For such tags, the closing message does not
|
|
* appear when later reaching the end of stream. The exception is
|
|
* the <head> tag: the parser is notified about its silent closing
|
|
* when <body> or other html content appears.
|
|
*/
|
|
protected void forciblyCloseDueContext()
|
|
{
|
|
forcibly_closed = true;
|
|
}
|
|
|
|
/**
|
|
* This is called when the tag must be forcibly closed after
|
|
* reaching the end of stream. The parser is notified as if
|
|
* closing the tag explicitly.
|
|
*/
|
|
protected void forciblyCloseDueEndOfStream()
|
|
{
|
|
forcibly_closed = true;
|
|
handleSupposedEndTag(element);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The DTD, providing information about the valid document structure.
|
|
*/
|
|
protected final DTD dtd;
|
|
|
|
/**
|
|
* The stack, holding the current tag context.
|
|
*/
|
|
protected final LinkedList stack = new LinkedList();
|
|
|
|
/**
|
|
* Creates a new tag stack, using the given DTD.
|
|
* @param a_dtd A DTD, providing the information about the valid
|
|
* tag content.
|
|
*/
|
|
public htmlValidator(DTD a_dtd)
|
|
{
|
|
dtd = a_dtd;
|
|
}
|
|
|
|
/**
|
|
* Close all opened tags (called at the end of parsing).
|
|
*/
|
|
public void closeAll()
|
|
{
|
|
hTag h;
|
|
while (!stack.isEmpty())
|
|
{
|
|
h = (hTag) stack.getLast();
|
|
if (!h.forcibly_closed && !h.element.omitEnd())
|
|
s_error("Unclosed <" + h.tag + ">, closing at the end of stream");
|
|
|
|
handleSupposedEndTag(h.element);
|
|
|
|
closeTag(h.tgElement);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove the given tag from the stack or (if found) from the list
|
|
* of the forcibly closed tags.
|
|
*/
|
|
public void closeTag(TagElement tElement)
|
|
{
|
|
HTML.Tag tag = tElement.getHTMLTag();
|
|
hTag x;
|
|
hTag close;
|
|
|
|
if (!stack.isEmpty())
|
|
{
|
|
ListIterator iter = stack.listIterator(stack.size());
|
|
|
|
while (iter.hasPrevious())
|
|
{
|
|
x = (hTag) iter.previous();
|
|
if (tag.equals(x.tag))
|
|
{
|
|
if (x.forcibly_closed && !x.element.omitEnd())
|
|
s_error("The tag <" + x.tag +
|
|
"> has already been forcibly closed"
|
|
);
|
|
|
|
|
|
// If the tag has a content model defined, forcibly close all
|
|
// tags that were opened after the tag being currently closed.
|
|
closing:
|
|
if (x.element.content != null)
|
|
{
|
|
iter = stack.listIterator(stack.size());
|
|
while (iter.hasPrevious())
|
|
{
|
|
close = (hTag) iter.previous();
|
|
if (close == x)
|
|
break closing;
|
|
handleSupposedEndTag(close.element);
|
|
iter.remove();
|
|
}
|
|
}
|
|
|
|
stack.remove(x);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
s_error("Closing unopened <" + tag + ">");
|
|
}
|
|
|
|
/**
|
|
* Add the given HTML tag to the stack of the opened tags. Forcibly closes
|
|
* all tags in the stack that does not allow this tag in they content (error
|
|
* is reported).
|
|
* @param element
|
|
*/
|
|
public void openTag(TagElement tElement, htmlAttributeSet parameters)
|
|
{
|
|
// If this is a fictional call, the message from the parser
|
|
// has recursively returned - ignore.
|
|
if (tElement.fictional())
|
|
return;
|
|
|
|
validateParameters(tElement, parameters);
|
|
|
|
// If the stack is empty, start from HTML
|
|
if (stack.isEmpty() && tElement.getHTMLTag() != HTML.Tag.HTML)
|
|
{
|
|
Element html = dtd.getElement(HTML.Tag.HTML.toString());
|
|
openFictionalTag(html);
|
|
}
|
|
|
|
Object v = tagIsValidForContext(tElement);
|
|
if (v != Boolean.TRUE)
|
|
{
|
|
// The tag is not valid for context, the content
|
|
// model suggest to open another tag.
|
|
if (v instanceof Element)
|
|
{
|
|
int n = 0;
|
|
while (v instanceof Element && (n++ < 100))
|
|
{
|
|
Element fe = (Element) v;
|
|
|
|
// notify the content model that we add the proposed tag
|
|
getCurrentContentModel().show(fe);
|
|
openFictionalTag(fe);
|
|
|
|
Object vv = tagIsValidForContext(tElement);
|
|
if (vv instanceof Element) // One level of nesting is supported.
|
|
{
|
|
openFictionalTag((Element) vv);
|
|
|
|
Object vx = tagIsValidForContext(tElement);
|
|
if (vx instanceof Element)
|
|
openFictionalTag((Element) vx);
|
|
}
|
|
else if (vv == Boolean.FALSE)
|
|
{
|
|
// The tag is still not valid for the current
|
|
// content after opening a fictional element.
|
|
if (fe.omitEnd())
|
|
{
|
|
// close the previously opened fictional tag.
|
|
closeLast();
|
|
vv = tagIsValidForContext(tElement);
|
|
if (vv instanceof Element)
|
|
|
|
// another tag was suggested by the content model
|
|
openFictionalTag((Element) vv);
|
|
}
|
|
}
|
|
v = tagIsValidForContext(tElement);
|
|
}
|
|
}
|
|
else // If the current element has the optional end tag, close it.
|
|
{
|
|
if (!stack.isEmpty())
|
|
{
|
|
closing:
|
|
do
|
|
{
|
|
hTag last = (hTag) stack.getLast();
|
|
if (last.element.omitEnd())
|
|
{
|
|
closeLast();
|
|
v = tagIsValidForContext(tElement);
|
|
if (v instanceof Element) // another tag was suggested by the content model
|
|
{
|
|
openFictionalTag((Element) v);
|
|
break closing;
|
|
}
|
|
}
|
|
else
|
|
break closing;
|
|
}
|
|
while (v == Boolean.FALSE && !stack.isEmpty());
|
|
}
|
|
}
|
|
}
|
|
|
|
stack.add(new hTag(tElement));
|
|
}
|
|
|
|
/**
|
|
* Clear the stack.
|
|
*/
|
|
public void restart()
|
|
{
|
|
stack.clear();
|
|
}
|
|
|
|
/**
|
|
* Check if this tag is valid for the current context.
|
|
* Return Boolean.True if it is OK, Boolean.False
|
|
* if it is surely not OK or the Element that the
|
|
* content model recommends to insert making the situation
|
|
* ok. If Boolean.True is returned, the content model current
|
|
* position is moved forward. Otherwise this position remains
|
|
* the same.
|
|
* @param tElement
|
|
* @return
|
|
*/
|
|
public Object tagIsValidForContext(TagElement tElement)
|
|
{
|
|
// Check the current content model, if one is available.
|
|
node cv = getCurrentContentModel();
|
|
|
|
if (cv != null)
|
|
return cv.show(tElement.getElement());
|
|
|
|
// Check exclusions and inclusions.
|
|
ListIterator iter = stack.listIterator(stack.size());
|
|
hTag t;
|
|
final int idx = tElement.getElement().index;
|
|
|
|
// Check only known tags.
|
|
if (idx >= 0)
|
|
{
|
|
BitSet inclusions = new BitSet();
|
|
while (iter.hasPrevious())
|
|
{
|
|
t = (hTag) iter.previous();
|
|
if (!t.forcibly_closed)
|
|
{
|
|
if (t.element.exclusions != null &&
|
|
t.element.exclusions.get(idx)
|
|
)
|
|
return Boolean.FALSE;
|
|
|
|
if (t.element.inclusions != null)
|
|
inclusions.or(t.element.inclusions);
|
|
}
|
|
}
|
|
if (!inclusions.get(idx))
|
|
return Boolean.FALSE;
|
|
}
|
|
return Boolean.TRUE;
|
|
}
|
|
|
|
/**
|
|
* Validate tag without storing in into the tag stack. This is called
|
|
* for the empty tags and results the subsequent calls to the openTag
|
|
* and closeTag.
|
|
*/
|
|
public void validateTag(TagElement tElement, htmlAttributeSet parameters)
|
|
{
|
|
openTag(tElement, parameters);
|
|
closeTag(tElement);
|
|
}
|
|
|
|
/**
|
|
* Check for mandatory elements, subsequent to the last tag:
|
|
* @param tElement The element that will be inserted next.
|
|
*/
|
|
protected void checkContentModel(TagElement tElement, boolean first)
|
|
{
|
|
if (stack.isEmpty())
|
|
return;
|
|
|
|
hTag last = (hTag) stack.getLast();
|
|
if (last.validationTrace == null)
|
|
return;
|
|
|
|
Object r = last.validationTrace.show(tElement.getElement());
|
|
if (r == Boolean.FALSE)
|
|
s_error("The <" + last.element + "> does not match the content model " +
|
|
last.validationTrace
|
|
);
|
|
else if (r instanceof Element) // The content model recommends insertion of this element
|
|
{
|
|
if (!first)
|
|
closeTag(last.tgElement);
|
|
handleSupposedStartTag((Element) r);
|
|
openTag(new TagElement((Element) r), null);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The method is called when the tag must be closed because
|
|
* it does not allow the subsequent elements inside its context
|
|
* or the end of stream has been reached. The parser is only
|
|
* informed if the element being closed does not require the
|
|
* end tag (the "omitEnd" flag is set).
|
|
* The closing message must be passed to the parser mechanism
|
|
* before passing message about the opening the next tag.
|
|
*
|
|
* @param element The tag being fictionally (forcibly) closed.
|
|
*/
|
|
protected abstract void handleSupposedEndTag(Element element);
|
|
|
|
/**
|
|
* The method is called when the validator decides to open the
|
|
* tag on its own initiative. This may happen if the content model
|
|
* includes the element with the optional (supposed) start tag.
|
|
*
|
|
* @param element The tag being opened.
|
|
*/
|
|
protected abstract void handleSupposedStartTag(Element element);
|
|
|
|
/**
|
|
* Handles the error message. This method must be overridden to pass
|
|
* the message where required.
|
|
* @param msg The message text.
|
|
*/
|
|
protected abstract void s_error(String msg);
|
|
|
|
/**
|
|
* Validate the parameters, report the error if the given parameter is
|
|
* not in the parameter set, valid for the given attribute. The information
|
|
* about the valid parameter set is taken from the Element, enclosed
|
|
* inside the tag. The method does not validate the default parameters.
|
|
* @param tag The tag
|
|
* @param parameters The parameters of this tag.
|
|
*/
|
|
protected void validateParameters(TagElement tag, htmlAttributeSet parameters)
|
|
{
|
|
if (parameters == null ||
|
|
parameters == htmlAttributeSet.EMPTY_HTML_ATTRIBUTE_SET ||
|
|
parameters == SimpleAttributeSet.EMPTY
|
|
)
|
|
return;
|
|
|
|
Enumeration enumeration = parameters.getAttributeNames();
|
|
|
|
while (enumeration.hasMoreElements())
|
|
{
|
|
validateAttribute(tag, parameters, enumeration);
|
|
}
|
|
|
|
// Check for missing required values.
|
|
AttributeList a = tag.getElement().getAttributes();
|
|
|
|
while (a != null)
|
|
{
|
|
if (a.getModifier() == DTDConstants.REQUIRED)
|
|
if (parameters.getAttribute(a.getName()) == null)
|
|
{
|
|
s_error("Missing required attribute '" + a.getName() + "' for <" +
|
|
tag.getHTMLTag() + ">"
|
|
);
|
|
}
|
|
a = a.next;
|
|
}
|
|
}
|
|
|
|
private node getCurrentContentModel()
|
|
{
|
|
if (!stack.isEmpty())
|
|
{
|
|
hTag last = (hTag) stack.getLast();
|
|
return last.validationTrace;
|
|
}
|
|
else
|
|
return null;
|
|
}
|
|
|
|
private void closeLast()
|
|
{
|
|
handleSupposedEndTag(((hTag) stack.getLast()).element);
|
|
stack.removeLast();
|
|
}
|
|
|
|
private void openFictionalTag(Element e)
|
|
{
|
|
handleSupposedStartTag(e);
|
|
stack.add(new hTag(new TagElement(e, true)));
|
|
if (!e.omitStart())
|
|
s_error("<" + e + "> is expected (supposing it)");
|
|
}
|
|
|
|
private void validateAttribute(TagElement tag, htmlAttributeSet parameters,
|
|
Enumeration enumeration
|
|
)
|
|
{
|
|
Object foundAttribute;
|
|
AttributeList dtdAttribute;
|
|
foundAttribute = enumeration.nextElement();
|
|
dtdAttribute = tag.getElement().getAttribute(foundAttribute.toString());
|
|
if (dtdAttribute == null)
|
|
{
|
|
StringBuffer valid =
|
|
new StringBuffer("The tag <" + tag.getHTMLTag() +
|
|
"> cannot contain the attribute '" + foundAttribute +
|
|
"'. The valid attributes for this tag are: "
|
|
);
|
|
|
|
AttributeList a = tag.getElement().getAttributes();
|
|
|
|
while (a != null)
|
|
{
|
|
valid.append(a.name.toUpperCase());
|
|
valid.append(' ');
|
|
a = a.next;
|
|
}
|
|
s_error(valid.toString());
|
|
}
|
|
|
|
else
|
|
{
|
|
String value = parameters.getAttribute(foundAttribute).toString();
|
|
|
|
if (dtdAttribute.type == DTDConstants.NUMBER)
|
|
validateNumberAttribute(tag, foundAttribute, value);
|
|
|
|
if (dtdAttribute.type == DTDConstants.NAME ||
|
|
dtdAttribute.type == DTDConstants.ID
|
|
)
|
|
validateNameOrIdAttribute(tag, foundAttribute, value);
|
|
|
|
if (dtdAttribute.values != null)
|
|
validateAttributeWithValueList(tag, foundAttribute, dtdAttribute,
|
|
value
|
|
);
|
|
}
|
|
}
|
|
|
|
private void validateAttributeWithValueList(TagElement tag,
|
|
Object foundAttribute,
|
|
AttributeList dtdAttribute,
|
|
String value
|
|
)
|
|
{
|
|
if (!dtdAttribute.values.contains(value.toLowerCase()) &&
|
|
!dtdAttribute.values.contains(value.toUpperCase())
|
|
)
|
|
{
|
|
StringBuffer valid;
|
|
if (dtdAttribute.values.size() == 1)
|
|
valid =
|
|
new StringBuffer("The attribute '" + foundAttribute +
|
|
"' of the tag <" + tag.getHTMLTag() +
|
|
"> cannot have the value '" + value +
|
|
"'. The only valid value is "
|
|
);
|
|
else
|
|
valid =
|
|
new StringBuffer("The attribute '" + foundAttribute +
|
|
"' of the tag <" + tag.getHTMLTag() +
|
|
"> cannot have the value '" + value + "'. The " +
|
|
dtdAttribute.values.size() +
|
|
" valid values are: "
|
|
);
|
|
|
|
Enumeration vv = dtdAttribute.values.elements();
|
|
while (vv.hasMoreElements())
|
|
{
|
|
valid.append('"');
|
|
valid.append(vv.nextElement());
|
|
valid.append("\" ");
|
|
}
|
|
s_error(valid.toString());
|
|
}
|
|
}
|
|
|
|
private void validateNameOrIdAttribute(TagElement tag, Object foundAttribute,
|
|
String value
|
|
)
|
|
{
|
|
boolean ok = true;
|
|
|
|
if (!Character.isLetter(value.charAt(0)))
|
|
ok = false;
|
|
|
|
char c;
|
|
for (int i = 0; i < value.length(); i++)
|
|
{
|
|
c = value.charAt(i);
|
|
if (!(
|
|
Character.isLetter(c) || Character.isDigit(c) ||
|
|
"".indexOf(c) >= 0
|
|
)
|
|
)
|
|
ok = false;
|
|
}
|
|
if (!ok)
|
|
s_error("The '" + foundAttribute + "' attribute of the tag <" +
|
|
tag.getHTMLTag() + "> must start from letter and consist of " +
|
|
"letters, digits, hypens, colons, underscores and periods. " +
|
|
"It cannot be '" + value + "'"
|
|
);
|
|
}
|
|
|
|
private void validateNumberAttribute(TagElement tag, Object foundAttribute,
|
|
String value
|
|
)
|
|
{
|
|
try
|
|
{
|
|
Integer.parseInt(value);
|
|
}
|
|
catch (NumberFormatException ex)
|
|
{
|
|
s_error("The '" + foundAttribute + "' attribute of the tag <" +
|
|
tag.getHTMLTag() + "> must be a valid number and not '" +
|
|
value + "'"
|
|
);
|
|
}
|
|
}
|
|
}
|