/*
* $Header: /export/home/cvsroot/MyPersonalizerRepository/MyPersonalizer/Subsystems/Kernel/Sources/es/udc/mypersonalizer/kernel/model/xmlconverters/VirtualMetaPropertiesXMLConverter.java,v 1.1.1.1 2004/03/25 12:08:37 fbellas Exp $
* $Revision: 1.1.1.1 $
* $Date: 2004/03/25 12:08:37 $
*
* =============================================================================
*
* Copyright (c) 2003, The MyPersonalizer Development Group
* (http://www.tic.udc.es/~fbellas/mypersonalizer/index.html) at
* University Of A Corua
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of the University Of A Corua nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
package es.udc.mypersonalizer.kernel.model.xmlconverters;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import es.udc.mypersonalizer.kernel.model.annotators.Annotations;
import es.udc.mypersonalizer.kernel.model.annotators.sql.SQLPersistenceTypeAnnotationHelper;
import es.udc.mypersonalizer.kernel.model.metainfo.MetaProperty;
import es.udc.mypersonalizer.kernel.model.metainfo.MetaService;
import es.udc.mypersonalizer.kernel.model.metainfo.MetaServiceRegistrySingleton;
import es.udc.mypersonalizer.kernel.model.query.virtualmetainfo.
LinkMetaPropertyAnnotationHelper;
import es.udc.mypersonalizer.kernel.model.query.virtualmetainfo.
VirtualMetaPropertyAnnotationHelper;
import es.udc.mypersonalizer.kernel.util.exceptions.InternalErrorException;
import es.udc.mypersonalizer.kernel.util.general.Cloner;
import es.udc.mypersonalizer.kernel.util.xml.DefaultErrorHandler;
import es.udc.mypersonalizer.kernel.util.xml.LocalEntityResolver;
import es.udc.mypersonalizer.kernel.util.xml.WarningsHandler;
/**
* Utility class that converts an XML file defining the virtual metaproperties
* in a map of virtual metaproperties indexed by <i>unique name</i>.
* <p>
* A <i>unique name</i> is similar to a <code>MetaProperty</code> scoped name,
* but replacing the root metaproperty name with the service identifier.
* <p>
* Some additional checks are made to avoid illegal declarations:
* <ul>
* <li>Virtual metaproperties can't have the same unique name as any other
* metaproperty (real or virtual).</li>
* <li>Virtual metaproperties links must reference exisiting metaservices and
* metaproperties.</li>
* </ul>
*
* @author Abel Muinho
* @since 1.0
*/
public class VirtualMetaPropertiesXMLConverter {
/** DTD's <code>publicId</code>. */
public static String DTD_PUBLIC_ID =
"-//MyPersonalizer//"
+ "DTD MyPersonalizer Virtual Metaproperties Configuration 1.0//EN";
/** File name to resolve DTD's <code>publicId</code>. */
public static String DTD_LOCAL_FILE_NAME =
"myper-virtual-metaproperties-config_1_0.dtd";
/** Name of the node containing the definition of virtual metaproperties.
* Value: <code>virtual-metaproperties</code>.
*/
private static final String VIRTUAL_PROPERTIES_NODE =
"virtual-metaproperties";
/** Name of the node containing the definition of a virtual property
* linking to a metaservice.
* Value: <code>metaservice-link</code>.
*/
private static final String METASERVICE_LINK_NODE = "metaservice-link";
private static final String UNIQUE_NAME_ATTR = "uniqueName";
private static final String SOURCE_PROPERTY_ATTR = "sourceProperty";
private static final String TARGET_ID_ATTR = "targetId";
private static final String TARGET_METASERVICE_ATTR = "metaService";
/** Hidden constructor. */
private VirtualMetaPropertiesXMLConverter() {
/* Hide the constructor. */
}
/**
* Converts the XML document read from the reader object into a map
* of virtual metaproperties.
*
* @param is the input stream providing the XML
* @param warningsHandler the handler for XML parsing messages.
* @return the map of virtual metaproperties indexed by unique name.
* @throws SAXException if a parsing error occurs.
* @throws InternalErrorException if a severe error occurs.
*/
public static Map fromXML(InputStream is, WarningsHandler warningsHandler)
throws SAXException, InternalErrorException {
try {
Document document =
createDocument(
is,
new DefaultErrorHandler(warningsHandler));
return buildVirtualMetaProperties(document.getDocumentElement());
} catch (SAXException e) {
throw e;
} catch (Exception e) {
throw new InternalErrorException(e);
}
}
/**
* Constructs a DOM Document corresponding to XML virtual metaproperties
* definition provided by a input stream. The definition will be validated.
* So, it must contain a reference to the corresponding DTD (or the DTD
* itself embedded in the service specification). For error handling,
* this method requires the parameter <code>errorHandler</code>,
* specifying an instance of a concrete class implementing the interface
* <code>ErrorHandler</code>.
*
* @param inputStream the input stream providing the XML service
* specification
* @param errorHandler an error handler
* @return the DOM Document corresponding to XML service specification
* given in the input stream
* @throws IOException if an I/O error occurs when reading from the
* input stream
* @throws ParserConfigurationException if there was a configuration
* problem related to the parser being used
* @throws SAXException if any parse error occurs. It will be an
* instance of <code>org.xml.sax.SAXParseException</code> if the
* error is because of the document not being well-formed or the
* constraints formally specified in the DTD are violated.
* Otherwise, it will be an instance of <code>SAXException</code>,
* representing a violated constraint not formally specified in
* the DTD (for example: a tag taking an illegal value).
*/
/* TODO: Check if this code can be refactored. It is copied from
* MetaServiceXMLConverter, and a similar function exists in other
* converters.
*/
private static Document createDocument(
InputStream inputStream,
ErrorHandler errorHandler)
throws IOException, ParserConfigurationException, SAXException {
/* Get a DOM parser factory. */
DocumentBuilderFactory documentBuilderFactory =
DocumentBuilderFactory.newInstance();
/* Configure the factory:
*
* 1. Enable validation.
* 2. Make CDATA sections transparent to our code.
* 3. Ignore comments
* 4. Discard ignorable whitespace.
*
* NOTE: entity references are expanded by default.
*/
documentBuilderFactory.setValidating(true);
documentBuilderFactory.setCoalescing(true);
documentBuilderFactory.setIgnoringComments(true);
documentBuilderFactory.setIgnoringElementContentWhitespace(true);
/* Create a DOM parser instance. */
DocumentBuilder documentBuilder =
documentBuilderFactory.newDocumentBuilder();
/* Configure the parser. */
documentBuilder.setErrorHandler(errorHandler);
documentBuilder.setEntityResolver(
new LocalEntityResolver(DTD_PUBLIC_ID, DTD_LOCAL_FILE_NAME));
/* Get the "Document" object. */
return documentBuilder.parse(inputStream);
}
/**
* Creates the virtual metaproperties map from the given node.
* @see #VIRTUAL_PROPERTIES_NODE
*
* @param node the node to process.
* @return the map of virtual properties indexed by unique name.
* @throws SAXException if errors parsing the XML are found.
* @throws InternalErrorException if severe errors occur.
*/
private static Map buildVirtualMetaProperties(Node node)
throws SAXException, InternalErrorException {
Map result = new HashMap();
NodeList children = node.getChildNodes();
int childrenCount = children.getLength();
for (int i = 0; i < childrenCount; i++) {
IdentifiedMetaProperty pair =
buildVirtualMetaProperty(children.item(i));
result.put(pair.getUniqueName(), pair.getMetaProperty());
}
return result;
}
/**
* Multiplexing function. Idetifies responsible for processing
* the given node to build a <code>MetaProperty</code> from it and
* forwards the request.
* <p>
* TODO: Use the Command pattern if this starts to grow.
* @param node the node for building a virtual metaproperty.
* @return the virtual metaproperty with the associated unique name.
* @throws SAXException if parsing errors are found.
* @throws InternalErrorException if severe errors are found.
*/
private static IdentifiedMetaProperty buildVirtualMetaProperty(Node node)
throws SAXException, InternalErrorException {
String nodeName = node.getNodeName();
MetaProperty mp;
if (nodeName.equals(METASERVICE_LINK_NODE)) {
mp = buildMetaServiceLink(node);
} else {
throw new IllegalArgumentException(
nodeName + " tag is not recognized.");
}
/* We know uniqueName is not null (it is validated using the DTD) */
String uniqueName =
node.getAttributes().getNamedItem(UNIQUE_NAME_ATTR).getNodeValue();
/* Tag the metaproperty as virtual. */
mp.getAnnotations().set(
VirtualMetaPropertyAnnotationHelper.VIRTUAL_ANNOTATION,
nodeName);
return new IdentifiedMetaProperty(uniqueName, mp);
}
/**
* Creates and annotates a metaproperty linking to a metaservice.
* @param node the node describing the link.
* @return the metaproperty representing the link.
* @throws SAXException if the referenced target metaproperty or
* metaservice can't be found.
* @throws InternalErrorException if a copy of the target metaproperty
* can't be found.
*/
private static MetaProperty buildMetaServiceLink(Node node)
throws InternalErrorException, SAXException {
NamedNodeMap attrs = node.getAttributes();
/* We know this won't be null (they are validated using the DTD. */
String sourceMetaProperty =
attrs.getNamedItem(SOURCE_PROPERTY_ATTR).getNodeValue();
String targetMetaProperty =
attrs.getNamedItem(TARGET_ID_ATTR).getNodeValue();
String targetMetaService =
attrs.getNamedItem(TARGET_METASERVICE_ATTR).getNodeValue();
MetaService metaService =
MetaServiceRegistrySingleton.getInstance().getMetaService(
targetMetaService);
/* Check that the metaservice exists. */
if (metaService == null) {
throw new SAXException(
"Target metaservice " + targetMetaService + " is not known.");
}
/* Get the metaservice's table. It'll be the metaproperty's too. */
String targetTable =
SQLPersistenceTypeAnnotationHelper.getTableNameAnnotation(
metaService);
/* Copy the target service metaproperty. */
MetaProperty metaProperty;
try {
metaProperty =
(MetaProperty)Cloner.clone(metaService.getMetaRootProperty());
} catch (IOException e) {
throw new InternalErrorException(e);
}
/* Add annotations for the link. */
Annotations annotations = metaProperty.getAnnotations();
annotations.set(
LinkMetaPropertyAnnotationHelper.SOURCE_PROPERTY_ANNOTATION,
sourceMetaProperty);
annotations.set(
LinkMetaPropertyAnnotationHelper.TARGET_ID_ANNOTATION,
targetMetaProperty);
annotations.set(
LinkMetaPropertyAnnotationHelper.TARGET_METASERVICE_ANNOTATION,
targetMetaService);
annotations.set(
SQLPersistenceTypeAnnotationHelper.TABLE_NAME_ANNOTATION,
targetTable);
metaProperty.setAnnotations(annotations);
return metaProperty;
}
}
/**
* Helper class to maintains associations between a virtual MetaProperty
* and its unique name.
*
* @author Abel Muinho
* @since 1.0
*/
class IdentifiedMetaProperty {
private String uniqueName;
private MetaProperty metaProperty;
/**
* Creates an metaproperty and associated unique name pair.
* @param uniqueName the unique name. Can't be <code>null</code>.
* @param metaProperty the metaproperty. Can't be <code>null</code>.
*/
public IdentifiedMetaProperty(String uniqueName, MetaProperty metaProperty) {
if (uniqueName == null || metaProperty == null) {
throw new IllegalArgumentException();
}
this.uniqueName = uniqueName;
this.metaProperty = metaProperty;
}
/**
* Obtains the unique name from the pair.
* @return the unique name.
*/
public String getUniqueName() {
return uniqueName;
}
/**
* Obtains the metaproperty from the pair.
* @return the metaproperty.
*/
public MetaProperty getMetaProperty() {
return metaProperty;
}
}
|