org.dita.dost.writer.DitaIndexWriter.java Source code

Java tutorial

Introduction

Here is the source code for org.dita.dost.writer.DitaIndexWriter.java

Source

/*
 * This file is part of the DITA Open Toolkit project.
 * See the accompanying license.txt file for applicable licenses.
 */

/*
 * (c) Copyright IBM Corp. 2004, 2005 All Rights Reserved.
 */
package org.dita.dost.writer;

import static org.apache.commons.io.FileUtils.*;
import static org.dita.dost.util.Constants.*;
import static org.dita.dost.util.XMLUtils.*;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import org.dita.dost.exception.DITAOTXMLErrorHandler;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

/*
 * Created on 2004-12-17
 */

/**
 * DitaIndexWriter reads dita topic file and insert the index information into it.
 * 
 * @author Zhang, Yuan Peng
 */
public final class DitaIndexWriter extends AbstractXMLWriter {

    /** whether we have met <metadata> in <prolog> element */
    private boolean hasMetadataTillNow;
    /** whether we have met <prolog> in this topic we want */
    private boolean hasPrologTillNow;

    private String indexEntries;
    /** topic path that topicIdList need to match */
    private List<String> matchList;
    private OutputStreamWriter output;
    private final XMLReader reader;
    /** whether to insert links at this topic */
    private boolean startTopic;
    /** array list that is used to keep the hierarchy of topic id */
    private final List<String> topicIdList;
    private boolean hasWritten;
    private int topicLevel = -1;

    /**
     * Default constructor of DitaIndexWriter class.
     */
    public DitaIndexWriter() {
        super();
        topicIdList = new ArrayList<>(16);
        hasMetadataTillNow = false;
        hasPrologTillNow = false;
        indexEntries = null;
        matchList = null;
        output = null;
        startTopic = false;
        hasWritten = false;

        try {
            reader = getXMLReader();
            reader.setContentHandler(this);
            reader.setFeature(FEATURE_NAMESPACE_PREFIX, true);
        } catch (final Exception e) {
            throw new RuntimeException("Failed to initialize XML parser: " + e.getMessage(), e);
        }

    }

    @Override
    public void characters(final char[] ch, final int start, final int length) throws SAXException {
        try {
            writeCharacters(ch, start, length);
        } catch (final IOException e) {
            logger.error(e.getMessage(), e);
        }
    }

    /**
     * check whether the hierarchy of current node match the matchList
     */
    private boolean checkMatch() {
        if (matchList == null) {
            return true;
        }
        final int matchSize = matchList.size();
        final int ancestorSize = topicIdList.size();
        final List<String> tail = topicIdList.subList(ancestorSize - matchSize, ancestorSize);
        return matchList.equals(tail);
    }

    @Override
    public void endDocument() throws SAXException {

        try {
            output.flush();
        } catch (final Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    @Override
    public void endElement(final String uri, final String localName, final String qName) throws SAXException {
        if (!startTopic) {
            topicIdList.remove(topicIdList.size() - 1);
        }
        try {
            if (!hasMetadataTillNow && TOPIC_PROLOG.localName.equals(qName) && startTopic && !hasWritten) {

                writeStartElement(TOPIC_METADATA.localName,
                        new AttributesBuilder().add(ATTRIBUTE_NAME_CLASS, TOPIC_METADATA.toString()).build());
                output.write(indexEntries);
                writeEndElement(TOPIC_METADATA.localName);
                hasMetadataTillNow = true;
                hasWritten = true;
            }
            writeEndElement(qName);
            if (!hasPrologTillNow && startTopic && !hasWritten) {
                // if <prolog> don't exist
                writeStartElement(TOPIC_PROLOG.localName,
                        new AttributesBuilder().add(ATTRIBUTE_NAME_CLASS, TOPIC_PROLOG.toString()).build());
                writeStartElement(TOPIC_METADATA.localName,
                        new AttributesBuilder().add(ATTRIBUTE_NAME_CLASS, TOPIC_METADATA.toString()).build());
                output.write(indexEntries);
                writeEndElement(TOPIC_METADATA.localName);
                writeEndElement(TOPIC_PROLOG.localName);
                hasPrologTillNow = true;
                hasWritten = true;
            }

        } catch (final Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    private boolean hasMetadata(final String qName) {
        //check whether there is <metadata> in <prolog> element
        //if there is <prolog> element and there is no <metadata> element before
        //and current element is <resourceid>, then there is no <metadata> in current
        //<prolog> element. return false.
        return !(hasPrologTillNow && !hasMetadataTillNow && TOPIC_RESOURCEID.localName.equals(qName));
    }

    private boolean hasProlog(final Attributes atts) {
        //check whether there is <prolog> in the current topic
        //if current element is <body> and there is no <prolog> before
        //then this topic has no <prolog> and return false

        if (atts.getValue(ATTRIBUTE_NAME_CLASS) != null) {
            if (!hasPrologTillNow) {
                if (atts.getValue(ATTRIBUTE_NAME_CLASS).contains(TOPIC_BODY.matcher)) {
                    return false;
                } else if (atts.getValue(ATTRIBUTE_NAME_CLASS).contains(TOPIC_RELATED_LINKS.matcher)) {
                    return false;
                } else if (atts.getValue(ATTRIBUTE_NAME_CLASS).contains(TOPIC_TOPIC.matcher)) {

                    if (topicLevel > 0) {
                        topicLevel++;
                    } else if (topicLevel == -1) {
                        topicLevel = 1;
                    } else {
                        return false;
                    }
                    return false; //Eric

                }
            }
        }
        return true;
    }

    @Override
    public void ignorableWhitespace(final char[] ch, final int start, final int length) throws SAXException {
        try {
            writeCharacters(ch, start, length);
        } catch (final Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    @Override
    public void processingInstruction(final String target, final String data) throws SAXException {
        try {
            writeProcessingInstruction(target, data);
        } catch (final IOException e) {
            logger.error(e.getMessage(), e);
        }
    }

    public void setIndexEntries(final String indexEntries) {
        this.indexEntries = indexEntries;
    }

    private void setMatch(final String match) {
        int index = 0;
        matchList = new ArrayList<>(16);

        while (index != -1) {
            final int end = match.indexOf(SLASH, index);
            if (end == -1) {
                matchList.add(match.substring(index));
                index = end;
            } else {
                matchList.add(match.substring(index, end));
                index = end + 1;
            }
        }
    }

    @Override
    public void startElement(final String uri, final String localName, final String qName, final Attributes atts)
            throws SAXException {
        final int attsLen = atts.getLength();

        try {
            if (topicLevel != -1) {
                if (!hasProlog(atts) && startTopic && !hasWritten) {
                    // if <prolog> don't exist
                    writeStartElement(TOPIC_PROLOG.localName,
                            new AttributesBuilder().add(ATTRIBUTE_NAME_CLASS, TOPIC_PROLOG.toString()).build());
                    writeStartElement(TOPIC_METADATA.localName,
                            new AttributesBuilder().add(ATTRIBUTE_NAME_CLASS, TOPIC_METADATA.toString()).build());
                    output.write(indexEntries);
                    writeEndElement(TOPIC_METADATA.localName);
                    writeEndElement(TOPIC_PROLOG.localName);
                    hasPrologTillNow = true;
                    hasWritten = true;
                }
            }
            if (!startTopic && !ELEMENT_NAME_DITA.equals(qName)) {
                if (atts.getValue(ATTRIBUTE_NAME_ID) != null) {
                    topicIdList.add(atts.getValue(ATTRIBUTE_NAME_ID));
                } else {
                    topicIdList.add("null");
                }
                if (topicIdList.size() >= matchList.size()) {
                    //To access topic by id globally
                    startTopic = checkMatch();
                }
            }

            if (!hasMetadata(qName) && startTopic && !hasWritten) {
                writeStartElement(TOPIC_METADATA.localName,
                        new AttributesBuilder().add(ATTRIBUTE_NAME_CLASS, TOPIC_METADATA.toString()).build());
                output.write(indexEntries);
                writeEndElement(TOPIC_METADATA.localName);
                hasMetadataTillNow = true;
                hasWritten = true;
            }

            writeStartElement(qName, atts);

            if (atts.getValue(ATTRIBUTE_NAME_CLASS) != null) {

                if (atts.getValue(ATTRIBUTE_NAME_CLASS).contains(TOPIC_METADATA.matcher) && startTopic
                        && !hasWritten) {
                    hasMetadataTillNow = true;
                    output.write(indexEntries);
                    hasWritten = true;
                }
                if (atts.getValue(ATTRIBUTE_NAME_CLASS).contains(TOPIC_PROLOG.matcher)) {
                    hasPrologTillNow = true;
                }
            }
        } catch (final Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    @Override
    public void write(final File outputFilename) {
        String filename = outputFilename.getAbsolutePath();
        String file = null;
        String topic = null;
        File inputFile = null;
        File outputFile = null;

        try {
            if (filename.endsWith(SHARP)) {
                filename = filename.substring(0, filename.length() - 1);
            }

            if (filename.lastIndexOf(SHARP) != -1) {
                file = filename.substring(0, filename.lastIndexOf(SHARP));
                topic = filename.substring(filename.lastIndexOf(SHARP) + 1);
                setMatch(topic);
                startTopic = false;
            } else {
                file = filename;
                matchList = null;
                startTopic = true;
            }
            hasPrologTillNow = false;
            hasMetadataTillNow = false;
            hasWritten = false;
            inputFile = new File(file);
            outputFile = new File(file + FILE_EXTENSION_TEMP);
            output = new OutputStreamWriter(new FileOutputStream(outputFile), UTF8);

            topicIdList.clear();
            reader.setErrorHandler(new DITAOTXMLErrorHandler(file, logger));
            reader.parse(file);
        } catch (final Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            if (output != null) {
                try {
                    output.close();
                } catch (final Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }
        try {
            deleteQuietly(inputFile);
            moveFile(outputFile, inputFile);
        } catch (final Exception e) {
            logger.error("Failed to replace " + inputFile + ": " + e.getMessage());
        }
    }

    // SAX serializer methods

    private void writeStartElement(final String qName, final Attributes atts) throws IOException {
        final int attsLen = atts.getLength();
        output.write(LESS_THAN + qName);
        for (int i = 0; i < attsLen; i++) {
            final String attQName = atts.getQName(i);
            final String attValue = escapeXML(atts.getValue(i));
            output.write(STRING_BLANK + attQName + EQUAL + QUOTATION + attValue + QUOTATION);
        }
        output.write(GREATER_THAN);
    }

    private void writeEndElement(final String qName) throws IOException {
        output.write(LESS_THAN + SLASH + qName + GREATER_THAN);
    }

    private void writeCharacters(final char[] ch, final int start, final int length) throws IOException {
        output.write(escapeXML(ch, start, length));
    }

    private void writeProcessingInstruction(final String target, final String data) throws IOException {
        final String pi = data != null ? target + STRING_BLANK + data : target;
        output.write(LESS_THAN + QUESTION + pi + QUESTION + GREATER_THAN);
    }

}