package org.simantics.xml.sax; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import org.simantics.utils.datastructures.MapList; import org.simantics.xml.sax.configuration.Configuration; import org.w3._2001.xmlschema.Annotation; import org.w3._2001.xmlschema.Import; import org.w3._2001.xmlschema.Include; import org.w3._2001.xmlschema.OpenAttrs; import org.w3._2001.xmlschema.Schema; /** * This file is developed for XMpLant / Proteus schema conversion is not able to handle all XML Schema definitions. * * @author mlmarko * */ public class SchemaConverter { protected File outputPlugin; protected File schemaFile; protected File conversionFile; protected File ontologyFile; protected File parserDir; protected Schema schema; protected Configuration configuration; protected String pluginName; private String[] header; boolean createPGraph = true; boolean createImporter = true; boolean createExporter = true; private List parent = new ArrayList<>(); private List subConverters = new ArrayList<>(); private Map fileMap; private MapList schemaNSMap; private MapList shortNameMap; protected String schemaNs; protected String ontologyUri; protected String className; protected String name; protected String shortName; protected SchemaConversionBase base; private ManualSchemaFileImport fileImport; public SchemaConverter(File schemaFile, File conversionFile, File outputPlugin) throws IOException { this(null,schemaFile,conversionFile,outputPlugin); } public SchemaConverter(SchemaConverter parent,File schemaFile, File conversionFile, File outputPlugin) throws IOException { if (schemaFile == null || outputPlugin == null) throw new IllegalArgumentException(); this.outputPlugin = outputPlugin; this.schemaFile = schemaFile; this.conversionFile = conversionFile; pluginName = outputPlugin.getName(); String packageParts[] = pluginName.split("\\."); String outputLoc = outputPlugin.getAbsolutePath(); outputLoc += "/src"; for (String s : packageParts) outputLoc += "/"+s; String outputGraph = outputPlugin.getAbsolutePath(); outputGraph += "/graph"; outputGraph += "/" + schemaFile.getName().substring(0, schemaFile.getName().length()-4) +".pgraph"; this.ontologyFile = new File(outputGraph); this.parserDir = new File(outputLoc); if (parent != null) { this.parent.add(parent); parent.subConverters.add(this); } else { fileMap = new LinkedHashMap<>(); schemaNSMap = new MapList<>(); shortNameMap = new MapList<>(); } getRoot().fileMap.put(schemaFile.getAbsolutePath(), this); } public List getConverter(String schemaNS) { return getRoot().schemaNSMap.getValues(schemaNS); } public void setFileImport(ManualSchemaFileImport fileImport) { this.fileImport = fileImport; } public void setCreateExporter(boolean createExporter) { this.createExporter = createExporter; } public void setCreateImporter(boolean createImporter) { this.createImporter = createImporter; } public void setCreatePGraph(boolean createPGraph) { this.createPGraph = createPGraph; } protected SchemaConverter createSubConverter(String location, String ns) throws JAXBException, IOException { File directory = schemaFile.getParentFile(); File schemaFile = new File(directory.getAbsolutePath()+File.separator+location); if (!schemaFile.exists()) { if (getRoot().fileImport != null) { schemaFile = getRoot().fileImport.getFileForLocation(location); } if (!schemaFile.exists()) throw new FileNotFoundException(schemaFile.getAbsolutePath()); } SchemaConverter subConverter = getRoot().fileMap.get((schemaFile.getAbsolutePath())); if (subConverter == null) { subConverter = constructSubConverter(this, schemaFile, conversionFile, outputPlugin, ns); subConverter.createPGraph = this.createPGraph; subConverter.createImporter = this.createImporter; subConverter.createExporter = this.createExporter; } else { subConverter.parent.add(this); subConverters.add(subConverter); } return subConverter; } protected SchemaConverter constructSubConverter(SchemaConverter parent, File schemaFile, File conversionFile, File outputPlugin, String ns) throws IOException { return new SchemaConverter(parent,schemaFile, conversionFile, outputPlugin); } protected SchemaConverter getRoot() { if (this.fileMap != null) return this; Set processed = new HashSet<>(); return _getRoot(processed); } protected SchemaConverter _getRoot(Set processed) { if (processed.contains(this)) return null; if (this.fileMap != null) return this; processed.add(this); for (SchemaConverter sc : this.parent) { if (sc.fileMap != null) return sc; } for (SchemaConverter sc : this.parent) { SchemaConverter root = sc._getRoot(processed); if (root != null) return root; } return null; } public void convert() throws JAXBException, IOException { init(); for (SchemaConverter sc : getRoot().fileMap.values()) { sc.doConvert(); } } boolean init = false; protected void assignShortName() { shortName = name.substring(0, 3).toUpperCase(); SchemaConverter root = getRoot(); if (!root.shortNameMap.containsKey(shortName)) { root.shortNameMap.add(shortName, this); return; } else { SchemaConverter sc = root.shortNameMap.getValues(shortName).get(0); if (sc.schemaNs.equals(schemaNs)) { root.shortNameMap.add(shortName, this); return; } } int i = 1; while (true) { String n = shortName+i; if (!root.shortNameMap.containsKey(n)) { shortName = n; root.shortNameMap.add(shortName, this); return; } else { SchemaConverter sc = root.shortNameMap.getValues(n).get(0); if (sc.schemaNs.equals(schemaNs)) { shortName = n; root.shortNameMap.add(shortName, this); return; } } i++; } } protected Schema createSchema() throws JAXBException, FileNotFoundException { JAXBContext jc = JAXBContext.newInstance("org.w3._2001.xmlschema"); Unmarshaller u = jc.createUnmarshaller(); //u.setSchema(schema); InputStream fileStream = new FileInputStream(schemaFile); return (Schema)u.unmarshal(fileStream); } protected void init() throws IOException, JAXBException { if (init) return; init = true; schema = createSchema(); if (conversionFile != null) { JAXBContext jc = JAXBContext.newInstance("org.simantics.xml.sax.configuration"); Unmarshaller u = jc.createUnmarshaller(); InputStream fileStream = new FileInputStream(conversionFile); configuration = (Configuration)((JAXBElement)u.unmarshal(fileStream)).getValue(); } else { configuration = new Configuration(); } header = new String[4]; header[0] = "Generated with org.simantics.xml.sax XML schema converter"; header[1] = ""; header[2] = "File " + schemaFile.getAbsolutePath().replaceAll(Matcher.quoteReplacement("\\"), "/"); header[3] = "Date " + new Date().toString(); schemaNs = schema.getTargetNamespace(); ontologyUri = schemaNs; if (ontologyUri == null) { ontologyUri = getSchemaFile().getName(); int index = ontologyUri.lastIndexOf("."); if (index > 0) ontologyUri = ontologyUri.substring(0, index); schemaNs = ""; } else { // Special case for XAML if (ontologyUri.startsWith("clr-namespace:")) { ontologyUri = ontologyUri.substring("clr-namespace:".length()); int i = ontologyUri.indexOf(";assembly"); if (i > 0) ontologyUri = ontologyUri.substring(0, i); } } ontologyUri = ontologyUri.replaceAll(" ", "_"); ontologyUri = ontologyUri.replaceAll(":", "_"); ontologyUri = ontologyUri.replaceAll(";", "_"); String parts[] = ontologyUri.split("/"); for (int i = parts.length-1; i >= 0; i--) { name = parts[i]; if (!Character.isDigit(name.charAt(0))) break; } if (name == null) { throw new JAXBException("Could not resolve proper name for schema " + ontologyUri); } name = name.replaceAll("\\.", "_"); if (!ontologyUri.startsWith("http://")) ontologyUri = "http://" + ontologyUri.replaceAll("/", "_"); else ontologyUri = "http://" + ontologyUri.substring("http://".length()).replaceAll("/", "_"); String version = schema.getVersion(); if (version == null) version = "1.0"; ontologyUri +="-"+ version; className = getPluginName() + "." + name + "Ontology"; assignShortName(); if (schemaNs != null) getRoot().schemaNSMap.add(schemaNs, this); base = new SchemaConversionBase(this,ontologyUri,className); base.init(schema); for (OpenAttrs attrs : schema.getIncludeOrImportOrRedefine()) { if (attrs instanceof Import) { Import imp = (Import)attrs; String location = imp.getSchemaLocation(); SchemaConverter sc = createSubConverter(location, imp.getNamespace()); sc.init(); } else if (attrs instanceof Include) { Include inc = (Include)attrs; String location = inc.getSchemaLocation(); SchemaConverter sc = createSubConverter(location, null); sc.init(); } else if (attrs instanceof Annotation) { } else { throw new IOException("Cannot handle schema file " + schemaFile.getName() + ", the schema uses redefine elements."); } } } private boolean converting = false; protected void doConvert() throws IOException, JAXBException { if (converting) return; converting = true; if (!ontologyFile.exists()) { ontologyFile.getParentFile().mkdirs(); ontologyFile.createNewFile(); } if (!parserDir.exists()) parserDir.mkdirs(); if (createPGraph) { OntologyGenerator ontologyGenerator = new OntologyGenerator(this,base); ontologyGenerator.createOntology(); } if (createImporter) { ImporterGenerator importerGenerator = new ImporterGenerator(this,base); importerGenerator.createParser(); } if (createExporter) { ExporterGenerator exporterGenerator = new ExporterGenerator(this,base); exporterGenerator.createParser(); } base.component = null; } public File getOntologyFile() { return ontologyFile; } public File getParserDir() { return parserDir; } public Schema getSchema() { return schema; } public File getSchemaFile() { return schemaFile; } public String getPluginName() { return pluginName; } public String[] getHeader() { return header; } public Configuration getConfiguration() { return configuration; } public List getParent() { return parent; } public List getSubConverters() { return subConverters; } public boolean isPrimary() { return true; // if (getRoot() == this) // return true; // List conv = new ArrayList<>(getRoot().fileMap.values()); // int current = conv.indexOf(this); } public String getShortName(String namespaceURI) { List converters = getRoot().getConverter(namespaceURI); for (SchemaConverter conv : converters) { if (conv.shortName != null) return conv.shortName; } return null; } public String getOntologyClassName(String namespaceURI) { List converters = getRoot().getConverter(namespaceURI); for (SchemaConverter conv : converters) { if (conv.className != null) return conv.className; } return null; } }