Java tutorial
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 org.jdom2.Document; import org.jdom2.Element; import org.jdom2.ProcessingInstruction; import org.jdom2.output.Format; import org.jdom2.output.XMLOutputter; import java.io.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.text.SimpleDateFormat; import java.util.*; import static java.lang.System.*; /** * The {@link Writer} class provides the tools to write * odML metadata files. * * @since 08.2009 * * @author Jan Grewe, Christine Seitz, Jakub Krauz * */ public class Writer implements Serializable { private static final long serialVersionUID = 146L; private final boolean asTerminology; private Document doc; private final File file; private Section odmlTree = null; private final static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); private final static SimpleDateFormat datetimeFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); private final static SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mm:ss"); private String[] section_fields = { "type", "name", "definition", "repository", "mapping", "link", "include", "reference" }; private String[] property_fields = { "name", "definition", "dependency", "dependencyValue", "mapping" }; private String[] value_fields = { "content", "type", "unit", "uncertainty", "definition", "reference", "filename", "encoder", "checksum" }; /** * Creates a writer instance. Lets the Writer write only those properties that have values. * * @param rootSection {@link Section} the root Section of the metadata tree. * */ public Writer(Section rootSection) { this(rootSection, false); } /** * Creates a writer instance. Setting asTerminology to true lets the writer * write also those properties that have no values as is usually the case for * terminologies. * * @param odmlSection {@link Section}: the odmlSection of the odml metadata tree. * @param asTerminology {@link Boolean}: if true also empty properties (no value) are written in the serialization, * otherwise only non-empty properties are processed. * */ public Writer(Section odmlSection, boolean asTerminology) { this.odmlTree = odmlSection; this.asTerminology = asTerminology; this.file = null; } /** * Creates a writer instance. Lets the Writer write only those properties that have values. * * @param filename {@link String} the full name of the destination file (including path). * @param odmlSection {@link Section} the root Section of the metadata tree. * * @deprecated Use combination of {@link #Writer(Section)} and {@link #write(OutputStream)} instead. */ public Writer(String filename, Section odmlSection) { this(new File(filename), odmlSection); } /** * Creates a Writer-instance. Writes only non-empty properties into the metadata files. * * @param file {@link File} the File into which the metadata should be written. * @param odmlSection {@link Section}: the rootSection of the odml metadata tree. * * @deprecated Use combination of {@link #Writer(Section)} and {@link #write(OutputStream)} instead. */ @Deprecated public Writer(File file, Section odmlSection) { this(file, odmlSection, false); } /** * Creates a Writer-instance. Setting asTerminology to true lets the writer * write also those properties that have no values as is usually the case for * terminologies. * * @param file {@link File} the File into which the metadata should be written. * @param odmlSection {@link Section}: the rootSection of the odml metadata tree. * @param asTerminology {@link Boolean}: if true also empty properties (no value) are written, otherwise * only non-empty properties are written to disc. * * @deprecated Use combination of {@link #Writer(Section, boolean)} and {@link #write(OutputStream)} instead. */ @Deprecated public Writer(File file, Section odmlSection, boolean asTerminology) { this.file = file; this.odmlTree = odmlSection; this.asTerminology = asTerminology; } /** * Writes the odML serialization to a file with the given name. * * @param fileName {@link String}: the name of the output file * @return {@link Boolean} true if operation was successful, false otherwise. * */ public boolean write(String fileName) { if (odmlTree == null) { System.out.println("Writer.write error: there is no metadata to write!"); return false; } createDom(odmlTree, asTerminology); try { FileOutputStream stream = new FileOutputStream(fileName); return writeToStream(stream); } catch (FileNotFoundException e) { System.out.println(e.getMessage()); return false; } } /** * Writes the odML serialization to the given output stream. * * @param stream {@link OutputStream}: output stream to which to write the document * @return {@link Boolean} true if operation was successful, false otherwise. * */ public boolean write(OutputStream stream) { if (odmlTree == null) { System.out.println("Writer.write error: there is no metadata to write!"); return false; } createDom(odmlTree, asTerminology); return writeToStream(stream); } /** * Writes the odML serialization to the given output stream. The odML tree can be optimized * (linked sections are simplified to reduce redundancy) and validated against terminologies before * the serialization. * * @param stream {@link OutputStream}: output stream to which to write the document * @param optimize {@link Boolean}: remove empty properties and sections, removes redundancy in linked sections. * @param validate {@link Boolean}: validates the metadata against the terminologies. * @return {@link Boolean}: true if writing succeeded, false otherwise. * */ public boolean write(OutputStream stream, boolean optimize, boolean validate) { if (optimize) odmlTree.optimizeTree(); if (validate) odmlTree.validateTree(); return write(stream); } /** * Write the metadata to disc after the tree has been optimized (linked sections are simplified to reduce * redundancy) and validated against the terminologies. * * @param optimize {@link Boolean}: remove empty properties and sections, removes redundancy in linked sections. * @param validate {@link Boolean}: validates the metadata against the terminologies. * @return {@link Boolean}: true if writing succeeded, false otherwise. * * @deprecated Use {@link #write(OutputStream, boolean, boolean)} instead. */ @Deprecated public boolean write(boolean optimize, boolean validate) { if (optimize) { odmlTree.optimizeTree(); } if (validate) { odmlTree.validateTree(); } return write(); } /** * Writes the metadata to disc. * * @return {@link Boolean} true if operation was successful, false otherwise. * * @deprecated Use {@link #write(OutputStream)} instead. */ @Deprecated public boolean write() { if (odmlTree == null) { out.println("Writer.write error: there is no metadata to write!"); return false; } try { FileOutputStream stream = new FileOutputStream(file); createDom(odmlTree, asTerminology); return (write(stream)); } catch (Exception e) { System.out.println(e.getMessage()); return false; } } public Map<String, Object> getMap() { Map<String, Object> self = new HashMap<String, Object>(); self.put("date", odmlTree.getDocumentDate()); self.put("author", odmlTree.getDocumentAuthor()); self.put("version", odmlTree.getDocumentVersion()); self.put("repository", odmlTree.getRepository()); self.put("section", odmlTree.getMap()); return self; } /** * Creates the Dom document from the section. * * @param rootSection {@link Section}: the section to start the dom creation. * @param asTerminology {@link boolean}: flag to indicate whether Template is used or not * */ private void createDom(Section rootSection, boolean asTerminology) { doc = new Document(); ProcessingInstruction instruction; ProcessingInstruction alternativeInstruction; if (asTerminology) { alternativeInstruction = new ProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"odml.xsl\""); instruction = new ProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"odmlTerms.xsl\""); } else { alternativeInstruction = new ProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"odmlTerms.xsl\""); instruction = new ProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"odml.xsl\""); } doc.addContent(instruction); doc.addContent(alternativeInstruction); Element rootElement = new Element("odML"); rootElement.setAttribute("version", "1"); doc.setRootElement(rootElement); Section dummyRoot; if (rootSection.propertyCount() != 0) { dummyRoot = new Section(); dummyRoot.add(rootSection); dummyRoot.setDocumentAuthor(rootSection.getDocumentAuthor()); dummyRoot.setDocumentDate(rootSection.getDocumentDate()); dummyRoot.setDocumentVersion(rootSection.getDocumentVersion()); dummyRoot.setRepository(rootSection.getRepository()); } else { dummyRoot = rootSection; } String author = dummyRoot.getDocumentAuthor(); if (author != null) { Element authorElement = new Element("author"); authorElement.setText(author); rootElement.addContent(authorElement); } String version = dummyRoot.getDocumentVersion(); if (version != null) { Element versionElement = new Element("version"); versionElement.setText(version); rootElement.addContent(versionElement); } String dateString; Date date = dummyRoot.getDocumentDate(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); if (date != null) { dateString = sdf.format(date); } else { date = new Date(Calendar.getInstance().getTimeInMillis()); dateString = sdf.format(date); } Element dateElement = new Element("date"); dateElement.setText(dateString); rootElement.addContent(dateElement); URL repository = dummyRoot.getRepository(); if (repository != null) { Element repElement = new Element("repository"); repElement.setText(repository.toString()); rootElement.addContent(repElement); } for (int i = 0; i < dummyRoot.sectionCount(); i++) { appendSection(rootElement, dummyRoot.getSection(i), asTerminology); } } /** * Add a new {@link org.jdom2.Element} to a parent Element. If content is not null or empty the toString method is * invoked to store the content. * * @param parent - {@link org.jdom2.Element} the parent Element. * @param name - {@link java.lang.String} the new elements name. * @param content - {@link java.util.Objects} the content. */ private void addElement(Element parent, String name, Object content) { if (content == null || content.toString().isEmpty()) { return; } Element element = new Element(name); element.setText(content.toString()); parent.addContent(element); } /** * Returns the value of a field specified by the field name. Assuming a getter pattern like getFieldName * * @param entity - {@link java.lang.Object} the entity * @param fieldName - {@link java.lang.String} the field name * @return Object the value or null. */ private Object getFieldValue(Object entity, String fieldName) { for (Method method : entity.getClass().getDeclaredMethods()) { if (method.getName().toLowerCase().equals(("get" + fieldName).toLowerCase())) { try { return method.invoke(entity); } catch (IllegalAccessException e) { System.out.println("Could not invoke method: " + method.getName() + "on entity " + entity.getClass().toString()); } catch (InvocationTargetException e) { System.out.println("Could not invoke method: " + method.getName() + "on entity " + entity.getClass().toString()); } } } System.out.println("Could not find method " + "get" + fieldName + " in entity of type " + entity.getClass().toString() + " !"); return null; } /** * Method to append a section-element to the dom-tree. * @param parent {@link Element}: the parent where the section shall be appended * @param section {@link Section}: the section to append to the parent-element * @param asTemplate {@link boolean}: flag to indicate whether template or not; if template then also writing * value-information (e.g. unit or type) without having actual value-content */ private void appendSection(Element parent, Section section, boolean asTemplate) { Element sectionElement = new Element("section"); for (String section_field : section_fields) { addElement(sectionElement, section_field, getFieldValue(section, section_field)); } for (int i = 0; i < section.propertyCount(); i++) { appendProperty(sectionElement, section.getProperty(i), asTemplate); } for (int i = 0; i < section.sectionCount(); i++) { appendSection(sectionElement, section.getSection(i), asTemplate); } parent.addContent(sectionElement); } /** * Appends a property elements to the dom tree. Empty properties (those with no values) * will only be written to file if the file is to become a terminology. * * @param parent {@link Element}: the parent Element to which the properties belong. * @param property {@link Property}: the property to append. * @param asTerminology boolean: defines whether the file will be a terminology. */ private void appendProperty(Element parent, Property property, boolean asTerminology) { if (!asTerminology) { property.removeEmptyValues(); if (property.isEmpty()) { out.println("Writer.appendProperty: Property " + property.getName() + "is empty and will not be written to file!"); return; } } Element propertyElement = new Element("property"); for (String property_field : property_fields) { addElement(propertyElement, property_field, getFieldValue(property, property_field)); } for (int i = 0; i < property.valueCount(); i++) { appendValue(propertyElement, property.getWholeValue(i), asTerminology); } parent.addContent(propertyElement); } /** * Appends a value element to the dom tree. * * @param parent {@link Element}: the parent Element to which the values belong. * @param value {@link Value}: the value to append. * @param asTemplate defines whether to save as template, i.e. empty values are accepted. */ private void appendValue(Element parent, Value value, boolean asTemplate) { if (!asTemplate) { if (value.getContent() == null || value.getContent().toString().isEmpty()) { return; } } Element valueElement = new Element("value"); for (String value_field : value_fields) { Object content = getFieldValue(value, value_field); if (content instanceof Date) { if (value.getType().equalsIgnoreCase("date")) { content = dateFormat.format(content); } else if (value.getType().equalsIgnoreCase("datetime")) { content = datetimeFormat.format(content); } else if (value.getType().equalsIgnoreCase("time")) { content = timeFormat.format(content); } else { content = datetimeFormat.format(content); } } addElement(valueElement, value_field, content); } parent.addContent(valueElement); } /** * Writes the dom tree to the given output stream. * * @param stream the output stream * @return true if the dom tree was successfully written to the stream, false otherwise * */ private boolean writeToStream(OutputStream stream) { if (doc == null) { System.out.println("Writing to Stream failed, document is empty!"); return false; } try { org.jdom2.output.Format format = org.jdom2.output.Format.getPrettyFormat().setIndent(" "); XMLOutputter outputter = new XMLOutputter(); outputter.setFormat(Format.getPrettyFormat()); outputter.output(doc, stream); } catch (IOException ie) { System.out.println("Write to file failed: " + ie.getMessage()); return false; } System.out.println("Writing to file successful!"); return true; } }