Xercesと(NekoHTMLと)戯れる3
koichiさん、yoneさんのアドバイスで一歩前進(^o^)/
でもなんかDOCTYPEが出てないので、ちょと見直し。
むー。
(追記3)
HtmlNodeHandlerで、characters()でrootノードがない場合に
スキップしてました。
なので、rootノードがなくて、かつStackに何も積まれていない場合
DocTypeとしてみなすようにしました。
これでTestもう少しして、整えてコミットしたいと思います。
(追記4)
テストは足りてませんが、コードは晒しておきたいと思います。
Filterは、基本的にS2JSFのTextuallyXMLFilterをそのまま
もってきました。
DOCTYPEとコメントはそのまま出すようにしてます。
public class TeedaXMLDocumentFilterImpl implements XMLDocumentFilter { protected int inEntity; private XMLDocumentHandler orgHandler; public TeedaXMLDocumentFilterImpl() { } public void startElement(QName element, XMLAttributes attributes, Augmentations augs) throws XNIException { orgHandler.startElement(element, new TeedaXMLAttributesImpl( (XMLAttributesImpl) attributes), augs); } public void characters(XMLString text, Augmentations augs) throws XNIException { if (inEntity == 0) { orgHandler.characters(text, augs); } } public void comment(XMLString text, Augmentations augs) throws XNIException { final StringBuffer buf = new StringBuffer(text.length + 7); buf.append("<!--").append(text.ch, text.offset, text.length).append( "-->"); final String comment = new String(buf); orgHandler.characters(new XMLString(comment.toCharArray(), 0, comment .length()), augs); } public void doctypeDecl(String rootElement, String publicId, String systemId, Augmentations augs) throws XNIException { final StringBuffer buf = new StringBuffer(128); buf.append("<!DOCTYPE ").append(rootElement); if (publicId != null) { buf.append(" PUBLIC \"").append(publicId).append("\""); } if (systemId != null) { if (publicId == null) { buf.append(" SYSTEM"); } buf.append(" \"").append(systemId).append("\""); } buf.append(">\n"); final String docTypeDecl = new String(buf); orgHandler.characters(new XMLString(docTypeDecl.toCharArray(), 0, docTypeDecl.length()), augs); } public void emptyElement(QName element, XMLAttributes attributes, Augmentations augs) throws XNIException { orgHandler.emptyElement(element, attributes, augs); } public void endCDATA(Augmentations augs) throws XNIException { orgHandler.endCDATA(augs); } public void endDocument(Augmentations augs) throws XNIException { orgHandler.endDocument(augs); } public void endElement(QName element, Augmentations augs) throws XNIException { orgHandler.endElement(element, augs); } public void endGeneralEntity(String name, Augmentations augs) throws XNIException { --inEntity; } public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException { orgHandler.ignorableWhitespace(text, augs); } public void processingInstruction(String target, XMLString data, Augmentations augs) throws XNIException { orgHandler.processingInstruction(target, data, augs); } public void startCDATA(Augmentations augs) throws XNIException { orgHandler.startCDATA(augs); } public void startDocument(XMLLocator locator, String encoding, NamespaceContext namespaceContext, Augmentations augs) throws XNIException { orgHandler.startDocument(locator, encoding, namespaceContext, augs); } public void startGeneralEntity(String name, XMLResourceIdentifier identifier, String encoding, Augmentations augs) throws XNIException { final String entityRef = "&" + name + ";"; orgHandler.characters(new XMLString(entityRef.toCharArray(), 0, entityRef.length()), augs); ++inEntity; } public void textDecl(String version, String encoding, Augmentations augs) throws XNIException { orgHandler.textDecl(version, encoding, augs); } public void xmlDecl(String version, String encoding, String standalone, Augmentations augs) throws XNIException { orgHandler.xmlDecl(version, encoding, standalone, augs); } public XMLDocumentHandler getDocumentHandler() { return orgHandler; } public void setDocumentHandler(XMLDocumentHandler handler) { this.orgHandler = handler; } public XMLDocumentSource getDocumentSource() { return orgHandler.getDocumentSource(); } public void setDocumentSource(XMLDocumentSource source) { orgHandler.setDocumentSource(source); } }
XMLAttributesImplのgetValueだけをgetNonNormalizedValueの呼び出しに
変えてます。
public class TeedaXMLAttributesImpl extends XMLAttributesImpl { private XMLAttributesImpl attributes; public TeedaXMLAttributesImpl(XMLAttributesImpl attributes) { super(); this.attributes = attributes; } public String getValue(int index) { String value = attributes.getNonNormalizedValue(index); return value; } public String getValue(String qname) { int index = getIndex(qname); return index != -1 ? getNonNormalizedValue(index) : null; } public String getValue(String uri, String localName) { int index = getIndex(uri, localName); return index != -1 ? getNonNormalizedValue(index) : null; } あとはひたすら単なるgetter/setter }
あとのConfigurationのクラスとかは同じ。
/* * Copyright 2004-2006 the Seasar Foundation and the Others. * * 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 org.seasar.teeda.extension.html.impl; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; import org.seasar.framework.util.ResourceUtil; import org.seasar.teeda.core.JsfConstants; import org.seasar.teeda.extension.html.ElementNode; import org.seasar.teeda.extension.html.HtmlNode; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; public class HtmlNodeHandler extends DefaultHandler { private Stack nodeStack = new Stack(); private HtmlNode root; private Map dtdPaths = new HashMap(); private List elementNodeTagName = new ArrayList(); private Stack forceElementNodeStack = new Stack(); private static final String XHTML_DTD_RESOURCES_PATH = "org/seasar/teeda/extension/resource/xhtml1/"; private static final String HTML4_DTD_RESOURCES_PATH = "org/seasar/teeda/extension/resource/html4/"; private String maybeDocType; public HtmlNodeHandler() { initialize(); } public HtmlNode getRoot() { return root; } public void startElement(String namespaceURI, String localName, String qName, Attributes attributes) { Map props = HtmlNodeUtil.convertMap(attributes); if (root == null) { ElementNodeImpl n = new RootElementNodeImpl(qName, props, maybeDocType); root = n; push(n); maybeDocType = null; } else { ElementNodeImpl parent = peek(); if (isElementNode(parent, qName, attributes)) { ElementNodeImpl elementNode = new ElementNodeImpl(qName, props); parent.addElement(elementNode); push(elementNode); if (elementNodeTagName.contains(qName)) { forceElementNodeStack.push(elementNode); } } else { parent.addText(HtmlNodeUtil.getStartTagString(qName, props)); parent.incrementChildTextSize(); } } } private boolean isElementNode(ElementNode parent, String qName, Attributes attributes) { if (attributes.getValue(JsfConstants.ID_ATTR) != null) { return true; } if (!forceElementNodeStack.isEmpty()) { return true; } if (qName.equals(JsfConstants.INPUT_ELEM) && isTypeRadioOrCheckbox(attributes) && attributes.getValue(JsfConstants.NAME_ATTR) != null) { return true; } return false; } private boolean isTypeRadioOrCheckbox(Attributes attributes) { String value = attributes.getValue(JsfConstants.TYPE_ATTR); if (value == null) { return false; } return value.equalsIgnoreCase(JsfConstants.RADIO_VALUE) || value.equalsIgnoreCase(JsfConstants.CHECKBOX_VALUE); } public InputSource resolveEntity(String publicId, String systemId) throws SAXException { String dtdPath = null; if (publicId != null) { dtdPath = (String) dtdPaths.get(publicId); } if (dtdPath == null) { return null; } return new InputSource(ResourceUtil.getResourceAsStream(dtdPath)); } protected ElementNodeImpl peek() { return (ElementNodeImpl) nodeStack.peek(); } protected ElementNodeImpl pop() { return (ElementNodeImpl) nodeStack.pop(); } protected void push(ElementNodeImpl node) { nodeStack.push(node); } public void characters(char[] buffer, int start, int length) { if(root == null && nodeStack.isEmpty()){ maybeDocType = new String(buffer, start, length); } if (root == null) { return; } String text = new String(buffer, start, length); ElementNodeImpl tagNode = peek(); tagNode.addText(text); } public void endElement(String namespaceURI, String localName, String qName) { ElementNodeImpl current = peek(); if (current.getChildTextSize() == 0) { current.endElement(); pop(); if (!forceElementNodeStack.isEmpty() && current == forceElementNodeStack.peek()) { forceElementNodeStack.pop(); } } else { current.addText(HtmlNodeUtil.getEndTagString(qName)); current.decrementChildTextSize(); } } public void error(SAXParseException e) throws SAXException { throw e; } public void warning(SAXParseException e) throws SAXException { System.err.println(e); } public void registerDtdPath(String publicId, String dtdPath) { dtdPaths.put(publicId, dtdPath); } protected void initialize() { initializeDtdPathForXhtml1(); } protected void initializeDtdPathForXhtml1() { dtdPaths.put("-//W3C//DTD XHTML 1.0 Frameset//EN", XHTML_DTD_RESOURCES_PATH + "xhtml1-frameset.dtd"); dtdPaths.put("-//W3C//DTD XHTML 1.0 Strict//EN", XHTML_DTD_RESOURCES_PATH + "xhtml1-strict.dtd"); dtdPaths.put("-//W3C//DTD XHTML 1.0 Transitional//EN", XHTML_DTD_RESOURCES_PATH + "xhtml1-transitional.dtd"); dtdPaths.put("-//W3C//ENTITIES Latin 1 for XHTML//EN", XHTML_DTD_RESOURCES_PATH + "xhtml-lat1.ent"); dtdPaths.put("-//W3C//ENTITIES Symbols for XHTML//EN", XHTML_DTD_RESOURCES_PATH + "xhtml-symbol.ent"); dtdPaths.put("-//W3C//ENTITIES Special for XHTML//EN", XHTML_DTD_RESOURCES_PATH + "xhtml-special.ent"); } public void addElementNodeTagName(String tagName) { elementNodeTagName.add(tagName); } }
で、RootElementNodeだけ特別視。
DOCTYPE取るだけですけどねえ^^;
public class RootElementNodeImpl extends ElementNodeImpl { private String docType; public RootElementNodeImpl(String tagName, Map properties, String docType) { super(tagName, properties); this.docType = docType; } public String toString() { if (getChildSize() == 0) { return getEmptyTagString(); } StringBuffer buf = new StringBuffer(512); if(docType != null) { buf.append(docType); } buf.append(getStartTagString()); for (int i = 0; i < getChildSize(); ++i) { buf.append(getChild(i).toString()); } buf.append(getEndTagString()); return buf.toString(); } }