/*
* Copyright 2004-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.compass.core.converter.mapping.xsem;
import java.io.Reader;
import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.compass.core.CompassException;
import org.compass.core.Property;
import org.compass.core.Resource;
import org.compass.core.ResourceFactory;
import org.compass.core.config.CompassEnvironment;
import org.compass.core.config.CompassSettings;
import org.compass.core.converter.ConversionException;
import org.compass.core.converter.mapping.ResourceMappingConverter;
import org.compass.core.mapping.Mapping;
import org.compass.core.mapping.ResourceMapping;
import org.compass.core.mapping.xsem.XmlContentMapping;
import org.compass.core.mapping.xsem.XmlObjectMapping;
import org.compass.core.marshall.MarshallingContext;
import org.compass.core.spi.InternalResource;
import org.compass.core.spi.MultiResource;
import org.compass.core.xml.RawXmlObject;
import org.compass.core.xml.XmlObject;
/**
* Responsible for converting {@link XmlObject} based on {@link XmlObjectMapping}.
* <p/>
* Note, that marshalls might create several resources, if the {@link XmlObjectMapping} has
* an xpath expression associated with it.
* <p/>
*
* @author kimchy
*/
public class XmlObjectMappingConverter implements ResourceMappingConverter {
private Map<String, String> namespace;
public boolean marshall(Resource resource, Object root, Mapping mapping, MarshallingContext context) throws ConversionException {
extractNamespace(context);
// no need to marshall if it is null
if (root == null && !context.handleNulls()) {
return false;
}
if (root instanceof Resource) {
Resource rootResource = (Resource) root;
resource.copy(rootResource);
((InternalResource) resource).addUID();
return true;
}
XmlObjectMapping xmlObjectMapping = (XmlObjectMapping) mapping;
XmlObject rootXmlObject = (XmlObject) root;
rootXmlObject = getActualXmlObject(rootXmlObject, xmlObjectMapping, context, resource);
rootXmlObject.setNamespaces(namespace);
if (xmlObjectMapping.getXPath() != null) {
XmlObject[] xmlObjects = XmlConverterUtils.select(rootXmlObject, xmlObjectMapping);
if (xmlObjects == null || xmlObjects.length == 0) {
throw new ConversionException("xpath [" + xmlObjectMapping.getXPath() + "] returned no value for alias [" +
xmlObjectMapping.getAlias() + "]");
}
boolean store = false;
MultiResource multiResource = (MultiResource) resource;
multiResource.clear();
for (XmlObject xmlObject : xmlObjects) {
multiResource.addResource();
for (Iterator it = xmlObjectMapping.mappingsIt(); it.hasNext();) {
Mapping m = (Mapping) it.next();
InternalResource resource1 = (InternalResource) multiResource.currentResource();
store |= m.getConverter().marshall(resource1, xmlObject, m, context);
resource1.addUID();
}
}
return store;
} else {
boolean store = false;
for (Iterator it = xmlObjectMapping.mappingsIt(); it.hasNext();) {
Mapping m = (Mapping) it.next();
store |= m.getConverter().marshall(resource, rootXmlObject, m, context);
}
((InternalResource) resource).addUID();
return store;
}
}
public Object unmarshall(Resource resource, Mapping mapping, MarshallingContext context) throws ConversionException {
extractNamespace(context);
XmlObjectMapping xmlObjectMapping = (XmlObjectMapping) mapping;
if (xmlObjectMapping.getXmlContentMapping() == null) {
return null;
}
XmlContentMapping xmlContentMapping = xmlObjectMapping.getXmlContentMapping();
XmlObject xmlObject = (XmlObject) xmlContentMapping.getConverter().unmarshall(resource, xmlContentMapping, context);
xmlObject.setNamespaces(namespace);
return xmlObject;
}
public boolean marshallIds(Resource idResource, Object id, ResourceMapping resourceMapping, MarshallingContext context) throws ConversionException {
extractNamespace(context);
ResourceFactory resourceFactory = context.getResourceFactory();
XmlObjectMapping xmlObjectMapping = (XmlObjectMapping) resourceMapping;
Mapping[] ids = resourceMapping.getIdMappings();
if (id instanceof XmlObject) {
XmlObject rootXmlObject = getActualXmlObject((XmlObject) id, xmlObjectMapping, context, idResource);
rootXmlObject.setNamespaces(namespace);
if (xmlObjectMapping.getXPath() != null) {
XmlObject[] xmlObjects = XmlConverterUtils.select(rootXmlObject, xmlObjectMapping);
if (xmlObjects == null || xmlObjects.length == 0) {
throw new ConversionException("xpath [" + xmlObjectMapping.getXPath() + "] returned no value for alias [" +
xmlObjectMapping.getAlias() + "]");
}
MultiResource multiResource = (MultiResource) idResource;
multiResource.clear();
for (XmlObject xmlObject : xmlObjects) {
multiResource.addResource();
for (Mapping id1 : ids) {
id1.getConverter().marshall(multiResource.currentResource(), xmlObject, id1, context);
}
}
} else {
for (Mapping id1 : ids) {
id1.getConverter().marshall(idResource, rootXmlObject, id1, context);
}
}
} else if (id instanceof Resource) {
for (Mapping id1 : ids) {
Resource rId = (Resource) id;
idResource.addProperty(rId.getProperty(id1.getPath().getPath()));
}
} else if (id instanceof Object[]) {
if (Array.getLength(id) != ids.length) {
throw new ConversionException("Trying to load resource with [" + Array.getLength(id)
+ "] while has ids mappings of [" + ids.length + "]");
}
if (Property.class.isAssignableFrom(id.getClass().getComponentType())) {
for (int i = 0; i < ids.length; i++) {
idResource.addProperty((Property) Array.get(id, i));
}
} else {
for (int i = 0; i < ids.length; i++) {
idResource.addProperty(resourceFactory.createProperty(ids[i].getPath().getPath(), Array.get(id, i).toString(),
Property.Store.YES, Property.Index.NOT_ANALYZED));
}
}
} else {
if (ids.length != 1) {
throw new ConversionException(
"Trying to load resource which has more than one id mappings with only one id value");
}
if (id instanceof Property) {
idResource.addProperty((Property) id);
} else {
idResource.addProperty(resourceFactory.createProperty(ids[0].getPath().getPath(), id.toString(), Property.Store.YES,
Property.Index.NOT_ANALYZED));
}
}
((InternalResource) idResource).addUID();
return true;
}
public Object[] unmarshallIds(Object id, ResourceMapping resourceMapping, MarshallingContext context) throws ConversionException {
throw new ConversionException("Not supported, please use xml-content mapping");
}
private XmlObject getActualXmlObject(XmlObject rootXmlObject, XmlObjectMapping xmlObjectMapping, MarshallingContext context, Resource resource) {
// in case it is an xml string value, convert it into an xml object
if (rootXmlObject instanceof RawXmlObject) {
Reader xml = ((RawXmlObject) rootXmlObject).getXml();
XmlContentMapping xmlContentMapping = xmlObjectMapping.getXmlContentMapping();
XmlContentMappingConverter xmlContentMappingConverter;
if (xmlContentMapping != null) {
xmlContentMappingConverter = (XmlContentMappingConverter) xmlContentMapping.getConverter();
} else {
xmlContentMappingConverter = (XmlContentMappingConverter) context.getConverterLookup().
lookupConverter(CompassEnvironment.Converter.DefaultTypeNames.Mapping.XML_CONTENT_MAPPING);
}
rootXmlObject = xmlContentMappingConverter.getXmlContentConverter().fromXml(resource.getAlias(), xml);
}
return rootXmlObject;
}
private void extractNamespace(MarshallingContext context) throws CompassException {
if (namespace != null) {
return;
}
namespace = new HashMap<String, String>();
Map<String, CompassSettings> namespaceSettings = context.getSession().getSettings().getSettingGroups(CompassEnvironment.Xsem.Namespace.PREFIX);
if (namespaceSettings != null && !namespaceSettings.isEmpty()) {
for (Map.Entry<String, CompassSettings> entry : namespaceSettings.entrySet()) {
namespace.put(entry.getKey(), entry.getValue().getSetting(CompassEnvironment.Xsem.Namespace.URI));
}
}
}
}