/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* LabelsBuilder.java
* Copyright (C) 2009-2010 Aristotle University of Thessaloniki, Thessaloniki, Greece
*/
package mulan.data;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.helpers.DefaultValidationEventHandler;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import mulan.core.ArgumentNullException;
import org.xml.sax.SAXException;
/**
* The {@link LabelsBuilder} is responsible for creation of {@link LabelsMetaDataImpl} instance
* from specified XML file source. The builder ensures XML source validity against XML schema.
*
* @author Jozef Vilcek
*/
public final class LabelsBuilder {
private static final String LABELS_SCHEMA_SOURCE = "mulan/data/labels.xsd";
/** The namespace of the schema for label representation */
protected static final String LABELS_SCHEMA_NAMESPACE = "http://mulan.sourceforge.net/labels";
//private static final String LABELS_SCHEMA_LOCATION_ID = "http://mulan.sourceforge.net/schemata/labels.xsd";
private static final String SCHEMA_FULL_CHECKING_FEATURE = "http://apache.org/xml/features/validation/schema-full-checking";
/**
* Creates a {@link LabelsMetaData} instance from XML file specified by the path.
*
* @param xmlLabelsFilePath the path to XML file containing labels definition
* @return the {@link LabelsMetaData} instance
* @throws ArgumentNullException if specified path to XML file is null
* @throws IllegalArgumentException if file under specified path does not exists
* @throws LabelsBuilderException if specified file can not be read/opened
* @throws LabelsBuilderException if any error occur when validating XML against
* schema or when creating labels data
*/
public static LabelsMetaData createLabels(String xmlLabelsFilePath) throws LabelsBuilderException {
if (xmlLabelsFilePath == null) {
throw new ArgumentNullException("xmlLabelsFilePath");
}
File xmlDefFile = new File(xmlLabelsFilePath);
if (!xmlDefFile.exists()) {
throw new IllegalArgumentException(String.format(
"The specified XML file source '%s' does not exist.",
xmlDefFile.getAbsolutePath()));
}
LabelsMetaData result;
BufferedInputStream xmlFileInputStream = null;
try {
xmlFileInputStream = new BufferedInputStream(new FileInputStream(xmlDefFile));
result = createLabels(xmlFileInputStream);
} catch (FileNotFoundException e) {
throw new LabelsBuilderException(
String.format("Error when creating input stream for the file under path: '%s'.",
xmlLabelsFilePath));
} finally {
if (xmlFileInputStream != null) {
try {
xmlFileInputStream.close();
} catch (IOException e) {
}
}
}
return result;
}
/**
* Creates a {@link LabelsMetaData} instance from specified input stream.
*
* @param inputStream the input stream containing labels definition in XML format
* @return the {@link LabelsMetaData} instance
* @throws ArgumentNullException if specified input stream is null
* @throws LabelsBuilderException if any error occur when validating XML against
* schema or when creating labels data form specified input stream
*/
@SuppressWarnings("static-access")
public static LabelsMetaData createLabels(InputStream inputStream) throws LabelsBuilderException {
if (inputStream == null) {
throw new ArgumentNullException("inputStream");
}
LabelsMetaDataImpl result = null;
try {
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setFeature(SCHEMA_FULL_CHECKING_FEATURE, false);
Schema schema = schemaFactory.newSchema(LabelsBuilder.class.getClassLoader().getResource(LABELS_SCHEMA_SOURCE));
JAXBContext context = JAXBContext.newInstance(LabelsMetaDataImpl.class, LabelNodeImpl.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setEventHandler(new DefaultValidationEventHandler());
unmarshaller.setSchema(schema);
unmarshaller.setListener(new UnmarshallingProcessor());
result = (LabelsMetaDataImpl) unmarshaller.unmarshal(inputStream);
} catch (JAXBException exception) {
throw new LabelsBuilderException("Error when trying to create objects structure from XML source.",
exception);
} catch (SAXException exception) {
throw new LabelsBuilderException(
"Error when creating schema instance to validate the XML source for labels creation.",
exception);
}
return result;
}
/**
* Dumps specified labels meta-data into the file in XML format.
* If the file already exists, the content will be overwritten.
*
* @param labelsMetaData the meta-data which has to be dumped into the file
* @param xmlDumpFilePath the path to the file where meta-data should be dumped
* @throws LabelsBuilderException if specified file can not be read/opened
* @throws LabelsBuilderException if error occurs during the serialization of meta-data to
* the XML format of resulting XML is not valid against the schema
* @throws ArgumentNullException if path to the file is not specified
* @throws ArgumentNullException if specified labels meta-data is null
* @throws LabelsBuilderException if underlying implementation of specified labels meta-data is not supported
*/
public static void dumpLabels(LabelsMetaData labelsMetaData, String xmlDumpFilePath) throws LabelsBuilderException {
if (xmlDumpFilePath == null) {
throw new ArgumentNullException("xmlDumpFilePath");
}
File xmlDumpFile = new File(xmlDumpFilePath);
boolean fileExists = xmlDumpFile.exists();
BufferedOutputStream fileOutStream = null;
try {
if (!fileExists) {
xmlDumpFile.createNewFile();
}
fileOutStream = new BufferedOutputStream(new FileOutputStream(xmlDumpFile));
} catch (IOException exception) {
if (!fileExists) {
xmlDumpFile.delete();
}
throw new LabelsBuilderException("Error creating file output stream, to which labels meta-data has to be dumped.");
} finally {
if (fileOutStream != null) {
try {
fileOutStream.close();
} catch (IOException ex) {
}
}
}
}
/**
* Dumps specified labels meta-data, in XML format, into the specified {@link OutputStream}.
*
* @param labelsMetaData the meta-data which has to be dumped
* @param outputStream the output stream where XML dup will be written
* @throws LabelsBuilderException if error occurs during the serialization of meta-data to
* the XML format of resulting XML is not valid against the schema
* @throws ArgumentNullException if specified output strema is null.
* @throws ArgumentNullException if specified labels meta-data is null
* @throws LabelsBuilderException if underlying implementation of specified labels meta-data is not supported
*/
public static void dumpLabels(LabelsMetaData labelsMetaData, OutputStream outputStream) throws LabelsBuilderException {
if (outputStream == null) {
throw new ArgumentNullException("outputStream");
}
if (!(labelsMetaData instanceof LabelsMetaDataImpl)) {
throw new IllegalArgumentException(String.format("The specified implementation " +
"of labels meta data '%s' is not supported.", labelsMetaData.getClass().getName()));
}
try {
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setFeature(SCHEMA_FULL_CHECKING_FEATURE, false);
Schema schema = schemaFactory.newSchema(ClassLoader.getSystemResource(LABELS_SCHEMA_SOURCE));
JAXBContext context = JAXBContext.newInstance(LabelsMetaDataImpl.class, LabelNodeImpl.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setEventHandler(new DefaultValidationEventHandler());
marshaller.setSchema(schema);
marshaller.marshal(labelsMetaData, outputStream);
} catch (JAXBException exception) {
throw new LabelsBuilderException("Error when trying to dump labels meta-data objects structure to XML file.",
exception);
} catch (SAXException exception) {
throw new LabelsBuilderException(
"Error when creating schema instance to validate XML dump of labels meta-data objects structure.",
exception);
}
}
private static class UnmarshallingProcessor extends Unmarshaller.Listener {
@Override
public void afterUnmarshal(Object target, Object parent) {
if (parent instanceof LabelNodeImpl && target instanceof LabelNodeImpl) {
((LabelNodeImpl) target).setParent((LabelNodeImpl) parent);
}
if (target instanceof LabelsMetaDataImpl && parent == null) {
((LabelsMetaDataImpl) target).doReInit();
}
}
}
}