package com.alethis.xml;

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import javax.xml.transform.sax.*; 
import java.io.*;

public class ChainedContentHandlers {

    public static File INPUT_FILE = new File("c:\\project\\xml\\in.xml");
    public static File OUTPUT_FILE = new File("c:\\project\\xml\\out.xml");
    
    public final static void main(String[] args) {
        try {
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
            XMLFilter firstFilter = new ChainableFilter(new FirstContentHandler());            
            XMLFilter secondFilter = new ChainableFilter(new SecondContentHandler());
            XMLFilter thirdFilter = new ChainableFilter(new ThirdContentHandler());
            XMLFilter fourthFilter = new ChainableFilter(new FourthContentHandler());

            firstFilter.setParent(parser);
            secondFilter.setParent(firstFilter);
            thirdFilter.setParent(secondFilter);
            fourthFilter.setParent(thirdFilter);
 
            FileReader fr = new FileReader(INPUT_FILE);
            BufferedReader br = new BufferedReader(fr);
            InputSource is = new InputSource(br);
            SAXSource source = new SAXSource(fourthFilter, is);
            
            FileWriter fw = new FileWriter(OUTPUT_FILE);
            BufferedWriter bw = new BufferedWriter(fw);
            StreamResult result = new StreamResult(bw);

            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer();      
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");            
            transformer.transform(source, result);
            
            System.out.println("DONE");
        }
        catch (TransformerConfigurationException e) {
            System.out.println("Transformer Configuration Exception: " + e.getMessage());
        }
        catch (TransformerException e) {
            System.out.println("Transformer Exception: " + e.getMessage());
        }
        catch (IOException e) {
            System.out.println("IO Exception: " + e.getMessage());
        }           
        catch (SAXException se) {
            System.out.println("SAX exception: " + se.getMessage());
        }
    }
}

class ChainableFilter extends XMLFilterImpl {
    
    private ChainedContentHandler handler;
    
    public ChainableFilter(ChainedContentHandler handler) {
        this.handler = handler;
    }
    
    public void setContentHandler(ContentHandler nextHandler) {
        handler.setChildHandler(nextHandler);
        super.setContentHandler(handler);
    }
}

class ChainedContentHandler implements ContentHandler {
    
    protected ContentHandler childHandler;
    
    public void setChildHandler(ContentHandler childHandler) {
        this.childHandler = childHandler;
    }
    
    public void startElement(String namespaceURI, String localName, String qualifiedName, Attributes attrs) throws SAXException {
        childHandler.startElement(namespaceURI, localName, qualifiedName, attrs);
    }
    
    public void endElement(String namespaceURI, String localName, String qualifiedName) throws SAXException {
        childHandler.endElement(namespaceURI, localName, qualifiedName);
    }
    
    public void startDocument() throws SAXException {
        childHandler.startDocument();
    }
    
    public void endDocument() throws SAXException {
        childHandler.endDocument();
    }
    
    public void startPrefixMapping(String prefix, String uri) throws SAXException {
        childHandler.startPrefixMapping(prefix, uri);
    }
    
    public void endPrefixMapping(String prefix) throws SAXException {
        childHandler.endPrefixMapping(prefix);
    }
    
    public void characters(char[] text, int start, int length) throws SAXException {
        childHandler.characters(text, start, length);
    }
    
    public void ignorableWhitespace(char[] text, int start, int length) throws SAXException {
        childHandler.ignorableWhitespace(text, start, length);
    }
    
    public void processingInstruction(String target, String data) throws SAXException {
        childHandler.processingInstruction(target, data);
    }
    
    public void skippedEntity(String name) throws SAXException {
        childHandler.skippedEntity(name);
    }
    
    public void setDocumentLocator(Locator locator) {
        childHandler.setDocumentLocator(locator);
    }
}

class FirstContentHandler extends ChainedContentHandler {
    private static int id = 30;
    public void startElement(String namespaceURI, String localName, String qualifiedName, Attributes attrs) throws SAXException {
        AttributesImpl a = new AttributesImpl(attrs);
        a.addAttribute(null, "id", "id", "ID", "_" + String.valueOf(id++));
        childHandler.startElement(namespaceURI, localName, qualifiedName, a);
    }
}    

class SecondContentHandler extends ChainedContentHandler {
    public void startElement(String namespaceURI, String localName, String qualifiedName, Attributes attrs) throws SAXException {
        if ("children".equals(localName)) {
            localName = "kids";
            qualifiedName = "kids";
        }
        childHandler.startElement(namespaceURI, localName, qualifiedName, attrs);
    }
    public void endElement(String namespaceURI, String localName, String qualifiedName) throws SAXException {
        if ("children".equals(localName)) {
            localName = "kids";
            qualifiedName = "kids";
        }
        childHandler.endElement(namespaceURI, localName, qualifiedName);
    }
}    

class ThirdContentHandler extends ChainedContentHandler {
    public void startElement(String namespaceURI, String localName, String qualifiedName, Attributes attrs) throws SAXException {
        if ("child".equals(localName)) {
            localName = "kid";
            qualifiedName = "kid";
        }
        childHandler.startElement(namespaceURI, localName, qualifiedName, attrs);
    }
    public void endElement(String namespaceURI, String localName, String qualifiedName) throws SAXException {
        if ("child".equals(localName)) {
            localName = "kid";
            qualifiedName = "kid";
        }
        childHandler.endElement(namespaceURI, localName, qualifiedName);
    }
}    


class FourthContentHandler extends ChainedContentHandler {
    private boolean include = true;
    public void startElement(String namespaceURI, String localName, String qualifiedName, Attributes attrs) throws SAXException {
        if ("kids".equals(localName)) {
            include = false;
        }
        if (include) {
            childHandler.startElement(namespaceURI, localName, qualifiedName, attrs);
        }
    }
    public void endElement(String namespaceURI, String localName, String qualifiedName) throws SAXException {
        if (include) {
            childHandler.endElement(namespaceURI, localName, qualifiedName);
        }
        if ("kids".equals(localName)) {
            include = true;
        }        
    }
    public void characters(char[] text, int start, int length) throws SAXException {
        if (include) {
            childHandler.characters(text, start, length);
        }
    }
    public void ignorableWhitespace(char[] text, int start, int length) throws SAXException {
        if (include) {
            childHandler.ignorableWhitespace(text, start, length);
        }
    }
}