/*
* Copyright (c) 2010 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.rdfbean.schema;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import com.mysema.rdfbean.Namespaces;
import com.mysema.rdfbean.model.Format;
import com.mysema.rdfbean.model.MiniRepository;
import com.mysema.rdfbean.model.RDF;
import com.mysema.rdfbean.model.RDFS;
import com.mysema.rdfbean.model.Repository;
import com.mysema.rdfbean.model.RepositoryException;
import com.mysema.rdfbean.model.UID;
import com.mysema.rdfbean.object.Configuration;
import com.mysema.rdfbean.object.DefaultConfiguration;
import com.mysema.rdfbean.object.MappedClass;
import com.mysema.rdfbean.object.MappedPath;
import com.mysema.rdfbean.object.MappedPredicate;
import com.mysema.rdfbean.object.MappedProperty;
import com.mysema.rdfbean.object.Session;
import com.mysema.rdfbean.object.SessionFactoryImpl;
import com.mysema.rdfbean.owl.DatatypeProperty;
import com.mysema.rdfbean.owl.OWL;
import com.mysema.rdfbean.owl.OWLClass;
import com.mysema.rdfbean.owl.ObjectProperty;
import com.mysema.rdfbean.owl.Ontology;
import com.mysema.rdfbean.owl.Restriction;
import com.mysema.rdfbean.owl.TypedList;
import com.mysema.rdfbean.rdfs.RDFProperty;
import com.mysema.rdfbean.rdfs.RDFSClass;
import com.mysema.rdfbean.rdfs.RDFSDatatype;
import com.mysema.rdfbean.rdfs.RDFSResource;
import com.mysema.rdfbean.xsd.ConverterRegistry;
import com.mysema.rdfbean.xsd.ConverterRegistryImpl;
/**
* @author sasa
*
*/
public class SchemaGen {
private Configuration configuration;
private Repository repository = new MiniRepository();
private final Map<String, String> ns2prefix = new LinkedHashMap<String, String>(Namespaces.DEFAULT);
private final ConverterRegistry converterRegistry = new ConverterRegistryImpl();
private final Set<String> exportNamespaces = new HashSet<String>();
private boolean useTypedLists = true;
private String ontology;
private String[] ontologyImports;
public void export() {
SessionFactoryImpl sessionFactory = new SessionFactoryImpl();
sessionFactory.setConfiguration(new DefaultConfiguration(RDFSClass.class.getPackage(), OWLClass.class.getPackage()));
sessionFactory.setRepository(repository);
sessionFactory.initialize();
Session session = sessionFactory.openSession();
if (ontology != null) {
Ontology ont = new Ontology(new UID(ontology));
if (ontologyImports != null) {
for (String oimport : ontologyImports) {
ont.addImport(new Ontology(new UID(oimport)));
}
}
session.save(ont);
}
Map<UID, RDFSResource> resources = new HashMap<UID, RDFSResource>();
resources.put(RDF.List, new RDFSClass<RDFSResource>(RDF.List));
resources.put(RDF.first, new RDFProperty(RDF.first));
resources.put(RDF.rest, new RDFProperty(RDF.rest));
resources.put(RDF.type, new RDFProperty(RDF.type));
resources.put(RDFS.label, new RDFProperty(RDFS.label));
resources.put(RDFS.comment, new RDFProperty(RDFS.comment));
resources.put(RDFS.Resource, new RDFSClass<Object>(RDFS.Resource));
// process classes
for (MappedClass mappedClass : configuration.getMappedClasses()) {
processClass(session, mappedClass.getJavaClass(), resources);
}
// save resources
session.saveAll(resources.values().toArray());
}
public void export(Format format, OutputStream os) {
try {
export();
repository.export(format, ns2prefix, null, os);
} finally {
try {
os.close();
} catch (IOException e) {
throw new RepositoryException(e);
}
}
}
@SuppressWarnings("unchecked")
@Nullable
private RDFSClass<RDFSResource> processClass(Session session, Class<?> clazz, Map<UID, RDFSResource> resources) {
MappedClass mappedClass = configuration.getMappedClass(clazz);
OWLClass owlClass = null;
UID cuid = mappedClass.getUID();
if (cuid == null) {
return null;
}
if (!(exportNamespaces.isEmpty() || exportNamespaces.contains(cuid.ns()))) {
owlClass = new ReferenceClass(cuid);
} else {
owlClass = (OWLClass) resources.get(cuid);
if (owlClass == null) {
owlClass = new OWLClass(cuid);
resources.put(cuid, owlClass);
// label
owlClass.setLabel(Locale.ROOT, cuid.getLocalName());
} else {
return owlClass;
}
// super class
if (clazz.getSuperclass() != null && !clazz.getSuperclass().equals(Object.class)) {
addParent(clazz.getSuperclass(), owlClass, session, resources);
}
// interfaces
for (Class<?> iface : clazz.getInterfaces()) {
addParent(iface, owlClass, session, resources);
}
// enum instances
if (mappedClass.isEnum()) {
List<RDFSResource> oneOf = new ArrayList<RDFSResource>();
for (Object constant : clazz.getEnumConstants()) {
Enum enumValue = (Enum) constant;
oneOf.add(new AnyThing(new UID(cuid.ns(), enumValue.name()), owlClass, enumValue.ordinal()));
}
owlClass.setOneOf(oneOf);
}
// properties
for (MappedPath mappedPath : mappedClass.getProperties()) {
if (!mappedPath.isInherited() && mappedPath.isSimpleProperty()) {
processProperty(session, resources, owlClass, cuid, mappedPath);
}
}
}
return owlClass;
}
@SuppressWarnings("unchecked")
private void processProperty(Session session, Map<UID, RDFSResource> resources, OWLClass owlClass, UID cuid, MappedPath mappedPath) {
MappedProperty<?> mappedProperty = mappedPath.getMappedProperty();
MappedPredicate mappedPredicate = mappedPath.get(0);
UID puid = mappedPredicate.getUID();
String predicateNs = puid.ns();
// No restrictions on predicates from built-in namespaces
if (RDF.NS.equals(predicateNs) || RDFS.NS.equals(predicateNs) || OWL.NS.equals(predicateNs)) {
return;
}
final RDFProperty property;
boolean seenProperty = resources.containsKey(puid);
if (seenProperty) {
property = (RDFProperty) resources.get(puid);
if (mappedPath.isReference()) {
if (!(property instanceof ObjectProperty)) {
throw new IllegalArgumentException("Expected ObjectProperty for: " + mappedPath);
}
} else if (!mappedProperty.isAnyResource()) {
if (!(property instanceof DatatypeProperty)) {
throw new IllegalArgumentException("Expected DatatypeProperty for: " + mappedPath);
}
}
} else {
if (mappedProperty.isAnyResource()) {
property = new RDFProperty(puid);
} else if (mappedPath.isReference() || mappedProperty.isCollection()) {
property = new ObjectProperty(puid);
} else {
property = new DatatypeProperty(puid);
}
property.setLabel(Locale.ROOT, puid.getLocalName());
resources.put(puid, property);
// label
}
final Restriction restriction = new Restriction();
restriction.setOnProperty(property);
// TODO range mismatches!
if (mappedProperty.isCollection()) {
if (mappedProperty.isList()) {
if (property.getRange().isEmpty()) {
RDFSClass<?> componentType =
processClass(session, mappedProperty.getComponentType(), resources);
if (useTypedLists && componentType != null) {
property.addRange(new TypedList(cuid.ns(), componentType));
// Protege doesn't support typed lists using
// allValuesFrom
// owlClass.setAllValuesFrom(property, new
// TypedList(cuid.ns(), componentType));
} else {
owlClass.setAllValuesFrom(property, (RDFSClass<RDFSResource>) resources.get(RDF.List));
}
restriction.setMaxCardinality(1);
}
} else {
RDFSClass<?> componentType =
processClass(session, mappedProperty.getComponentType(),
resources);
if (componentType != null) {
restriction.setAllValuesFrom(componentType);
}
}
// reference
} else if (mappedPath.isReference()) {
if (property.getRange().isEmpty()) {
RDFSClass<?> range = processClass(session,
mappedProperty.getType(), resources);
if (range != null) {
owlClass.setAllValuesFrom(property, range);
} else if (mappedProperty.isAnyResource()) {
owlClass.setAllValuesFrom(property, (RDFSClass<?>) resources.get(RDFS.Resource));
}
}
restriction.setMaxCardinality(1);
// other
} else {
if (property.getRange().isEmpty()) {
UID range;
if (mappedProperty.isAnyResource()) {
range = RDFS.Resource;
} else if (mappedProperty.isLocalized()) {
range = RDF.text;
} else {
range = converterRegistry.getDatatype(mappedProperty.getType());
}
if (range != null) {
owlClass.setAllValuesFrom(property, getDatatype(range, resources));
}
}
restriction.setMaxCardinality(1);
}
if (restriction.isDefined()) {
owlClass.addSuperClass(restriction);
}
if (mappedProperty.isRequired()) {
final Restriction minCardinality = new Restriction();
minCardinality.setOnProperty(property);
minCardinality.setMinCardinality(1);
owlClass.addSuperClass(minCardinality);
}
}
private RDFSDatatype getDatatype(UID uid, Map<UID, RDFSResource> resources) {
if (resources.containsKey(uid)) {
return (RDFSDatatype) resources.get(uid);
} else {
RDFSDatatype datatype = new RDFSDatatype(uid);
resources.put(uid, datatype);
return datatype;
}
}
private void addParent(Class<?> clazz, OWLClass owlClass, Session session, Map<UID, RDFSResource> resources) {
RDFSClass<RDFSResource> parent = processClass(session, clazz, resources);
if (parent != null) {
owlClass.addSuperClass(parent);
}
}
public SchemaGen addExportNamespaces(Set<String> namespaces) {
exportNamespaces.addAll(namespaces);
return this;
}
public SchemaGen addExportNamespace(String ns) {
exportNamespaces.add(ns);
return this;
}
public SchemaGen setConfiguration(Configuration configuration) {
this.configuration = configuration;
return this;
}
public SchemaGen setOntology(String ontology) {
this.ontology = ontology;
return this;
}
public SchemaGen setUseTypedLists(boolean useTypedLists) {
this.useTypedLists = useTypedLists;
return this;
}
public SchemaGen setOntologyImports(String... ontologyImports) {
this.ontologyImports = ontologyImports;
return this;
}
public SchemaGen setNamespace(String prefix, String namespace) {
this.ns2prefix.put(namespace, prefix);
return this;
}
public SchemaGen setNamespaces(Map<String, String> namespaces) {
this.ns2prefix.putAll(namespaces);
return this;
}
public SchemaGen setRepository(Repository repository) {
this.repository = repository;
return this;
}
}