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();
    }
}