odml.core.Reader.java Source code

Java tutorial

Introduction

Here is the source code for odml.core.Reader.java

Source

package odml.core;

/************************************************************************
 * odML - open metadata Markup Language - Copyright (C) 2009, 2010 Jan Grewe, Jan Benda
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
 * Public License (LGPL) as published by the Free Software Foundation; either version 3 of the License, or (at your
 * option) any later version.
 * 
 * odML 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 Lesser General Public License along with this software. If not, see
 * <http://www.gnu.org/licenses/>.
 */

import odml.util.Mapper;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.jdom2.input.sax.XMLReaderJDOMFactory;
import org.jdom2.input.sax.XMLReaderXSDFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;

/**
 * The {@link Reader} class reads an xml-file, applies the schema, if wanted and provides the tools to extract the
 * stored information.
 * 
 * @since 08.2009
 * 
 * @author Jan Grewe, Christine Seitz, Jakub Krauz
 *
 */
public class Reader implements Serializable {

    private static final long serialVersionUID = 146L;
    private Section root;
    private final URL[] schemaLocations;
    private final Vector<Section> links = new Vector<Section>();
    Vector<Section> includes = new Vector<Section>();
    private URL fileUrl;
    boolean loadIncludes = false;
    public static int NO_CONVERSION = 1, FULL_CONVERSION = 3, LOAD_AND_RESOLVE = 2, NO_VALIDATION = 4, VALIDATE = 5;

    public Reader() {
        this(null);
    }

    /**
     * Constructor
     * 
     * @param schemaLocations
     *            {@link URL}[]: the location of the validation schema.
     */
    public Reader(URL[] schemaLocations) {
        this.schemaLocations = schemaLocations;
    }

    /**
     * Reads the odML document from the given InputStream and returns the root section of the odML tree.
     * This method does not load includes, does not resolve links and does not apply mapping information.
     * 
     * @param stream the input stream
     * @return {@link Section} the root section of the metadata tree loaded from the input stream
     * @throws Exception
     * 
     */
    public Section load(InputStream stream) throws Exception {
        return load(stream, false);
    }

    /**
     * Reads the odML document from the given InputStream and returns the root section of the odML tree.
     * This method does not load includes, does not resolve links and does not apply mapping information.
     * It validates the tree if schemaLocations is given in the constructor {@link #Reader(URL[])}
     * 
     * @param stream the input stream
     * @param validate if true validates the tree using schema, else no validation is applied
     * @return {@link Section} the root section of the metadata tree loaded from the input stream
     * @throws Exception
     * 
     */
    public Section load(InputStream stream, boolean validate) throws Exception {
        return load(stream, NO_CONVERSION, validate);
    }

    /**
     * Reads a metadata file from the location specified in file parameter and returns the root section of the odml
     * tree. Function does not load includes, resolves links or applies mapping information. Behavior of the function
     * can be specified with the option parameter. Returns the root section of the odml tree.
     * 
     * @param file  {@link String} the file url.
     * @return {@link Section} the root section of the metadata tree stored in the file.
     * 
     * @throws Exception
     */
    public Section load(String file) throws Exception {
        return load(file, NO_CONVERSION, false);
    }

    /**
     * Reads a metadata file from the specified url. Function behavior can be controlled using the option parameter: <li>
     * NO_CONVERSION : 1. Does nothing else but reading the file and returning the odml tree as defined in the file.</li>
     * <li>LOAD_AND_RESOLVE: 2. Load the file and all external information (defined in the include element) and resolve
     * links.</li> <li>FULL_CONVERSION : 3. loads the file, load external information (defined in the include element)
     * and resolves links between sections and applies mappings.</li>
     * 
     * @param file
     *            {@link String} the url of the metadata file.
     * @param option
     *            {@link Integer} the reading options.
     * @return Section the root section of the metadata tree stored in the file.
     * @throws Exception
     */
    public Section load(String file, int option) throws Exception {
        return load(file, option, false);
    }

    /**
     * 
     * @param file a string containing the file name.
     * @param loadOption int, specifying the actions to take upon loading
     * @return {@link Section} the root section of the file.
     * @throws Exception
     */
    public Section load(String file, int loadOption, boolean validate) throws Exception {
        URL url;
        try {
            url = new URL(file);
        } catch (Exception e) {
            try {
                url = new File(file).toURI().toURL();
            } catch (Exception exc) {
                throw new Exception("Could not read from specified location! " + file);
            }
        }
        return load(url, loadOption, validate);
    }

    /**
     * Load the file that is identified with the passed {@link URL}.
     * 
     * @param fileURL  The URL of the file.
     * @param option load option as described in load(String ...)
     * @return {@link Section}: the root section of the loaded file.
     * @throws Exception
     */
    public Section load(URL fileURL, int option, boolean validate) throws Exception {
        this.fileUrl = fileURL;
        try {
            InputStream stream = fileURL.openStream();
            System.out.println("Parsing the xml file: " + fileURL.toString() + "...");
            return load(stream, option, validate);
        } catch (IOException e) {
            System.out.println("Could not open file at specified url: " + fileURL.toString()
                    + ". Verify connection! " + e.getMessage());
            return null;
        }
    }

    /**
     * Load the odML document from the given input stream.
     * 
     * @param stream the input stream
     * @param option defines the behaviour during load. See load(String, int) method
     * @param validate defines whether the file should be validated against a schema.
     * @return {@link Section}: the root section of the loaded file.
     * @throws Exception
     */
    public Section load(InputStream stream, int option, boolean validate) throws Exception {
        boolean isValid = true;
        Section s;
        Document dom = parseXML(stream);
        if (dom == null) {
            this.root = null;
            return null;
        }
        if (validate && schemaLocations != null) {
            isValid = validateXML(stream);
        }

        if (isValid) {
            createTree(dom);
        } else {
            System.out.println("Validation failed.");
        }

        if (option == LOAD_AND_RESOLVE || option == FULL_CONVERSION) {
            loadIncludes();
        }
        if (option == LOAD_AND_RESOLVE || option == FULL_CONVERSION) {
            resolveLinks();
        }
        if (option == FULL_CONVERSION) {
            s = map();
        } else {
            s = this.getRootSection();
        }

        return s;
    }

    /**
     * Converts the DOM representation of the metadata-file to the tree like odML structure.
     * 
     * @param dom - {@link Document}: the document to parse
     */
    public void createTree(Document dom) {
        root = new Section();
        if (dom == null) {
            return;
        }
        Element rootElement = dom.getRootElement();
        String odmlVersion = rootElement.getAttribute("version").getValue();
        if (Float.parseFloat(odmlVersion) != 1.0) {
            System.out.println("Can not handle odmlVersion: " + odmlVersion + " stopping further processing!");
            return;
        }

        String author = rootElement.getChildText("author");
        root.setDocumentAuthor(author);
        Date date;
        String temp = rootElement.getChildText("date");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        try {
            date = sdf.parse(temp);
        } catch (Exception e) {
            date = null;
        }
        root.setDocumentDate(date);
        String version = rootElement.getChildText("version");
        root.setDocumentVersion(version);
        URL url = null;
        temp = rootElement.getChildText("repository");
        if (temp != null && !temp.isEmpty()) {
            try {
                url = new URL(temp);
            } catch (Exception e) {
                System.out.println("Reader.parseSection.repository: " + e);
            }
        }
        root.setRepository(url);
        root.setFileUrl(this.fileUrl);

        for (Element domSection : rootElement.getChildren("section")) {
            if (rootElement.isAncestor(domSection)) {
                root.add(parseSection(domSection));
            }
        }
        confirmLinks(root);
    }

    /**
     * Parses the xml file and creates the DOM representation of it.
     * @param stream - an {@link java.io.InputStream}
     * @return Document - returns the Document (dom) representation of the xml-file or null if an error occurred.
     */
    private Document parseXML(InputStream stream) {
        if (stream == null) {
            return null;
        }
        try {
            SAXBuilder builder = new SAXBuilder();
            return builder.build(stream);
        } catch (IOException ioe) {
            System.out.println("Parsing failed! " + ioe.getMessage());
            return null;
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return null;
        }
    }

    /**
     * Validates the the metadata xml-file against a schema and returns true if the file is valid or false if validation
     * fails.
     * @param stream - the input stream.
     * @return - boolean true or false if validation succeeded or failed, respectively.
     */
    private boolean validateXML(InputStream stream) {
        for (URL schemaLocation : schemaLocations) {
            try {
                File xsdfile = new File("schema.xsd");
                XMLReaderJDOMFactory schemafac = new XMLReaderXSDFactory(xsdfile);
                SAXBuilder builder = new SAXBuilder(schemafac);
                Document validdoc = builder.build(stream);
            } catch (Exception e) {
                System.out.println(e.getMessage());
                return false;
            }
        }
        return true;
    }

    /**
     * Parses an xml section of the metadata file and returns it. Subsections are parsed in a recursive
     * manner.
     * 
     * @param domSection - {@link Element}: the section that is to parse
     * @return {@link Section}: the Section representation of the dom section
     */
    private Section parseSection(Element domSection) {
        String type = domSection.getChildText("type");
        String name = domSection.getChildText("name");
        String reference = domSection.getChildText("reference");
        String definition = domSection.getChildText("definition");
        URL mapURL = null;
        String temp = domSection.getChildText("mapping");
        if (temp != null && !temp.isEmpty()) {
            try {
                mapURL = new URL(temp);
            } catch (Exception e) {
                System.out.println("odml.core.Reader.parseSection.mappingURL handling: " + e.getMessage());
            }
        }

        URL url = null;
        temp = domSection.getChildText("repository");
        if (temp != null && !temp.isEmpty()) {
            try {
                url = new URL(domSection.getChildText("repository"));
            } catch (Exception e) {
                url = null;
                System.out.println("Reader.parseSection.repository: " + e.getMessage());
            }
        }
        String link = domSection.getChildText("link");
        String include = domSection.getChildText("include");
        Section section;
        try {
            section = new Section(name, type, reference);
            section.setDefinition(definition);
            section.setRepository(url);
            section.setMapping(mapURL);
            section.setLink(link, true);
            if (link != null) {
                links.add(section);
            }
            section.setInclude(include);
            if (include != null) {
                includes.add(section);
            }
        } catch (Exception e) {
            System.out.println("Reader.parseSection: exception while creating section: " + e.getMessage());
            return null;
        }
        Property tempProp;
        for (Element element : domSection.getChildren("property")) {
            section.add(parseProperty(element));
        }
        for (Element element : domSection.getChildren("section")) {
            section.add(parseSection(element));
        }
        return section;
    }

    /**
     * Parses property and creates the odMLProperty representation of it.
     * 
     * @param domProperty - {@link Element}: the Element to parse. (should be a property element)
     * @return {@link Property} the {@link Property} representation of this domElement
     */
    private Property parseProperty(Element domProperty) {
        String name;
        name = domProperty.getChildTextTrim("name");
        String dependency;
        String dependencyValue;
        String definition;
        URL mapURL = null;

        String temp = domProperty.getChildText("mapping");

        if (temp != null && !temp.isEmpty() && !temp.endsWith("?")) {
            try {
                mapURL = new URL(temp);
            } catch (Exception e) {
                System.out.println("odml.core.Reader.parseProperty.mappingURL handling: \n"
                        + " \t> tried to form URL out of: '" + domProperty.getChildText("mapping")
                        + "'\n\t= mapURL of Property named: " + name + e.getMessage());
            }
        }
        definition = domProperty.getChildText("definition");
        dependency = domProperty.getChildText("dependency");
        dependencyValue = domProperty.getChildText("dependencyValue");
        Vector<Value> tmpValues = new Vector<Value>();
        for (Element element : domProperty.getChildren("value")) {
            tmpValues.add(parseValue(element));
        }

        Property property;
        try {
            property = new Property(name, tmpValues, definition, dependency, dependencyValue, mapURL);
        } catch (Exception e) {
            System.out.println("odml.core.Reader.parseProperty: create new prop failed. " + e.getMessage());
            property = null;
        }
        return property;
    }

    /**
     * Parses value and creates the odMLValue representation of it.
     * 
     * @param domValue
     *            - {@link Element}: the Element to parse. (should be a value element)
     * @return {@link Value} the {@link Value} representation of this domElement
     */
    private Value parseValue(Element domValue) {
        Value value;

        String content;
        String unit;
        Object uncertainty;
        String type;
        String filename;
        String definition;
        String reference;
        String encoder;
        String checksum;
        content = domValue.getTextTrim();
        if (content == null) {
            content = "";
        }
        unit = domValue.getChildText("unit");
        uncertainty = domValue.getChildText("uncertainty");
        type = domValue.getChildText("type");
        filename = domValue.getChildText("filename");
        definition = domValue.getChildText("definition");
        reference = domValue.getChildText("reference");
        checksum = domValue.getChildText("checksum");
        encoder = domValue.getChildText("encoder");
        try {
            value = new Value(content, unit, uncertainty, type, filename, definition, reference, encoder, checksum);
        } catch (Exception e) {
            System.out.println("odml.core.Reader.parseValue: create Value failed. " + e.getMessage());
            return null;
        }
        return value;
    }

    /**
     * Returns the rootSection of the odMLTree, i.e. the root of type Section
     * 
     * @return - {@link Section} the root section of the odML Tree.
     */
    public Section getRootSection() {
        return root;
    }

    /**
     * Tries to confirm the links stored in the tree by trying to retreive the linkes sections. Links that could not be
     * validated are removed and an error message is generated.
     * 
     * @param root
     *            {@link Section} the root section of the odml - tree.
     */
    private void confirmLinks(Section root) {
        for (Section link : links) {
            if (link.getSection(link.getLink()) == null
                    && link.getType().equals(link.getSection(link.getLink()).getType())) {
                System.out.println("Reader.confirmLinks: The link stored in section '" + link.toString()
                        + "' could not be confirmed and was removed!");
                link.setLink(null, true);
            }
        }
    }

    /**
     * Loads all included files into the document. Sections can contain includes 
     * which are links to sections located in a separate file. Includes are specified
     * by an URL pointing to an odml file. This url may include a hash (#)followed by
     * the absolute path of the target section. 
     * When loading an included section, the section is extended by the content of target
     * section (including subsections and their properties). 
     */
    public void loadIncludes() {
        for (Section include : includes) {
            include.loadInclude();
        }
    }

    /**
     * Tries to apply the mappings, if there are any.
     * 
     * @throws Exception
     */
    public Section map() throws Exception {
        Mapper m = new Mapper(root);
        return m.map();
    }

    /**
     * Resolves all links that are stored in the tree. This means that the
     * linking section are extended with the content of the section that is
     * referenced in the linking section. Links can only be established
     *  within the same file.
     */
    public void resolveLinks() {
        root.resolveAllLinks();
    }

}