Java tutorial
package org.apache.taverna.scufl2.translator.t2flow; /* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. * */ import static java.util.Locale.ENGLISH; import static java.util.TimeZone.getTimeZone; import static java.util.UUID.randomUUID; import static java.util.logging.Level.SEVERE; import static java.util.logging.Level.WARNING; import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; import static org.apache.taverna.scufl2.api.container.WorkflowBundle.WORKFLOW_BUNDLE_ROOT; import static org.apache.taverna.scufl2.api.core.Workflow.WORKFLOW_ROOT; import static org.apache.taverna.scufl2.xml.t2flow.jaxb.LinkType.DATAFLOW; import static org.apache.taverna.scufl2.xml.t2flow.jaxb.LinkType.MERGE; import static org.apache.taverna.scufl2.xml.t2flow.jaxb.LinkType.PROCESSOR; import static org.apache.taverna.scufl2.xml.t2flow.jaxb.Role.TOP; import java.io.CharArrayWriter; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.ServiceLoader; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.w3c.dom.Element; import org.xml.sax.SAXException; import org.apache.taverna.scufl2.api.annotation.Annotation; import org.apache.taverna.scufl2.api.annotation.Revision; import org.apache.taverna.scufl2.api.common.Named; import org.apache.taverna.scufl2.api.common.NamedSet; import org.apache.taverna.scufl2.api.common.Scufl2Tools; import org.apache.taverna.scufl2.api.common.URITools; import org.apache.taverna.scufl2.api.common.WorkflowBean; import org.apache.taverna.scufl2.api.configurations.Configuration; import org.apache.taverna.scufl2.api.container.WorkflowBundle; import org.apache.taverna.scufl2.api.core.BlockingControlLink; import org.apache.taverna.scufl2.api.core.ControlLink; import org.apache.taverna.scufl2.api.core.DataLink; import org.apache.taverna.scufl2.api.core.Processor; import org.apache.taverna.scufl2.api.core.Workflow; import org.apache.taverna.scufl2.api.io.ReaderException; import org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyNode; import org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyStack; import org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyTopNode; import org.apache.taverna.scufl2.api.iterationstrategy.PortNode; import org.apache.taverna.scufl2.api.port.InputActivityPort; import org.apache.taverna.scufl2.api.port.InputProcessorPort; import org.apache.taverna.scufl2.api.port.InputWorkflowPort; import org.apache.taverna.scufl2.api.port.OutputActivityPort; import org.apache.taverna.scufl2.api.port.OutputProcessorPort; import org.apache.taverna.scufl2.api.port.OutputWorkflowPort; import org.apache.taverna.scufl2.api.port.ReceiverPort; import org.apache.taverna.scufl2.api.port.SenderPort; import org.apache.taverna.scufl2.api.profiles.ProcessorBinding; import org.apache.taverna.scufl2.api.profiles.ProcessorInputPortBinding; import org.apache.taverna.scufl2.api.profiles.ProcessorOutputPortBinding; import org.apache.taverna.scufl2.api.profiles.Profile; import org.apache.taverna.scufl2.xml.t2flow.jaxb.Activity; import org.apache.taverna.scufl2.xml.t2flow.jaxb.AnnotatedGranularDepthPort; import org.apache.taverna.scufl2.xml.t2flow.jaxb.AnnotatedGranularDepthPorts; import org.apache.taverna.scufl2.xml.t2flow.jaxb.AnnotatedPort; import org.apache.taverna.scufl2.xml.t2flow.jaxb.AnnotatedPorts; import org.apache.taverna.scufl2.xml.t2flow.jaxb.AnnotationAssertionImpl.NetSfTavernaT2AnnotationAnnotationAssertionImpl; import org.apache.taverna.scufl2.xml.t2flow.jaxb.AnnotationChain; import org.apache.taverna.scufl2.xml.t2flow.jaxb.Annotations; import org.apache.taverna.scufl2.xml.t2flow.jaxb.Condition; import org.apache.taverna.scufl2.xml.t2flow.jaxb.Conditions; import org.apache.taverna.scufl2.xml.t2flow.jaxb.ConfigBean; import org.apache.taverna.scufl2.xml.t2flow.jaxb.CrossProduct; import org.apache.taverna.scufl2.xml.t2flow.jaxb.Dataflow; import org.apache.taverna.scufl2.xml.t2flow.jaxb.Datalinks; import org.apache.taverna.scufl2.xml.t2flow.jaxb.DepthPort; import org.apache.taverna.scufl2.xml.t2flow.jaxb.DepthPorts; import org.apache.taverna.scufl2.xml.t2flow.jaxb.DispatchLayer; import org.apache.taverna.scufl2.xml.t2flow.jaxb.DispatchStack; import org.apache.taverna.scufl2.xml.t2flow.jaxb.DotProduct; import org.apache.taverna.scufl2.xml.t2flow.jaxb.GranularDepthPort; import org.apache.taverna.scufl2.xml.t2flow.jaxb.GranularDepthPorts; import org.apache.taverna.scufl2.xml.t2flow.jaxb.IterationNode; import org.apache.taverna.scufl2.xml.t2flow.jaxb.IterationNodeParent; import org.apache.taverna.scufl2.xml.t2flow.jaxb.Link; import org.apache.taverna.scufl2.xml.t2flow.jaxb.Mapping; import org.apache.taverna.scufl2.xml.t2flow.jaxb.ObjectFactory; import org.apache.taverna.scufl2.xml.t2flow.jaxb.PortProduct; import org.apache.taverna.scufl2.xml.t2flow.jaxb.Processors; import org.apache.taverna.scufl2.xml.t2flow.jaxb.Raven; import org.apache.taverna.scufl2.xml.t2flow.jaxb.TopIterationNode; import com.fasterxml.jackson.databind.node.ObjectNode; public class T2FlowParser { private static final URI INTERNAL_DISPATCH_PREFIX = URI .create("http://ns.taverna.org.uk/2010/scufl2/taverna/dispatchlayer/"); private static final String TEXT_TURTLE = "text/turtle"; private static final String SEMANTIC_ANNOTATION = "net.sf.taverna.t2.annotation.annotationbeans.SemanticAnnotation"; private static final String IDENTIFICATION_ASSERTION = "net.sf.taverna.t2.annotation.annotationbeans.IdentificationAssertion"; private static final String T2FLOW_EXTENDED_XSD = "xsd/t2flow-extended.xsd"; @SuppressWarnings("unused") private static final String T2FLOW_XSD = "xsd/t2flow.xsd"; private static final Logger logger = Logger.getLogger(T2FlowParser.class.getCanonicalName()); public static final URI ravenURI = URI.create("http://ns.taverna.org.uk/2010/xml/t2flow/raven/"); public static final URI configBeanURI = URI.create("http://ns.taverna.org.uk/2010/xml/t2flow/configbean/"); public static final URI t2flowParserURI = URI.create("http://ns.taverna.org.uk/2012/scufl2/t2flowParser"); // TODO: Find better example predicate public static final URI exampleDataURI = URI.create("http://biocatalogue.org/attribute/exampleData"); public static final String DEFAULT_PRODUCED_BY = "unspecified"; public static <T extends Named> T findNamed(Collection<T> namedObjects, String name) { for (T named : namedObjects) if (named.getName().equals(name)) return named; return null; } protected ThreadLocal<ParserState> parserState = new ThreadLocalParserState(); /** * A static class for the thread-local parser state. */ private static class ThreadLocalParserState extends ThreadLocal<ParserState> { @Override protected ParserState initialValue() { return new ParserState(); }; } private static Scufl2Tools scufl2Tools = new Scufl2Tools(); private static URITools uriTools = new URITools(); private static TransformerFactory transformerFactory; protected Set<T2Parser> t2Parsers = null; protected final JAXBContext jaxbContext; private boolean strict = false; private boolean validating = false; public final boolean isValidating() { return validating; } public final void setValidating(boolean validating) { this.validating = validating; } protected ServiceLoader<T2Parser> discoveredT2Parsers; protected final ThreadLocal<Unmarshaller> unmarshaller; private Map<String, URI> predicates; public T2FlowParser() throws JAXBException { jaxbContext = JAXBContext.newInstance(ObjectFactory.class); unmarshaller = new ThreadLocalUnmarshaller(jaxbContext); } /** * A static class for the thread-local unmarshaller. */ private static class ThreadLocalUnmarshaller extends ThreadLocal<Unmarshaller> { private final JAXBContext jaxbContext; ThreadLocalUnmarshaller(JAXBContext jaxbContext) { this.jaxbContext = jaxbContext; } @Override protected Unmarshaller initialValue() { try { return jaxbContext.createUnmarshaller(); } catch (JAXBException e) { logger.log(SEVERE, "Could not create unmarshaller", e); return null; } }; } protected ReceiverPort findReceiverPort(Workflow wf, Link sink) throws ReaderException { String portName = sink.getPort(); if (portName == null) throw new ReaderException("Port name not specified"); String processorName = sink.getProcessor(); if (processorName == null) { if (sink.getType().equals(PROCESSOR)) throw new ReaderException("Link type was processor, but no processor name found"); OutputWorkflowPort candidate = wf.getOutputPorts().getByName(portName); if (candidate == null) throw new ReaderException("Link to unknown workflow port " + portName); return candidate; } else { if (sink.getType().equals(DATAFLOW)) throw new ReaderException("Link type was dataflow, but processor name was found"); Processor processor = wf.getProcessors().getByName(processorName); if (processor == null) throw new ReaderException("Link to unknown processor " + processorName); InputProcessorPort candidate = processor.getInputPorts().getByName(portName); if (candidate == null) throw new ReaderException("Link to unknown port " + portName + " in " + processorName); return candidate; } } protected SenderPort findSenderPort(Workflow wf, Link source) throws ReaderException { if (source.getType().equals(MERGE)) throw new ReaderException("Link type Merge unexpected for sender ports"); String portName = source.getPort(); if (portName == null) throw new ReaderException("Port name not specified"); String processorName = source.getProcessor(); if (processorName == null) { if (source.getType().equals(PROCESSOR)) throw new ReaderException("Link type was processor, but no processor name found"); InputWorkflowPort candidate = wf.getInputPorts().getByName(portName); if (candidate == null) throw new ReaderException("Link from unknown workflow port " + portName); return candidate; } else { if (source.getType().equals(DATAFLOW)) throw new ReaderException("Link type was dataflow, but processor name was found"); Processor processor = wf.getProcessors().getByName(processorName); if (processor == null) throw new ReaderException("Link from unknown processor " + processorName); OutputProcessorPort candidate = processor.getOutputPorts().getByName(portName); if (candidate == null) throw new ReaderException("Link from unknown port " + portName + " in " + processorName); return candidate; } } protected T2Parser getT2Parser(URI classURI) { for (T2Parser t2Parser : getT2Parsers()) if (t2Parser.canHandlePlugin(classURI)) return t2Parser; return null; } public synchronized Set<T2Parser> getT2Parsers() { Set<T2Parser> parsers = t2Parsers; if (parsers != null) return parsers; parsers = new HashSet<>(); /* * TODO: Do we need to cache this, or is the cache in ServiceLoader fast * enough? */ if (discoveredT2Parsers == null) discoveredT2Parsers = ServiceLoader.load(T2Parser.class); for (T2Parser parser : discoveredT2Parsers) parsers.add(parser); return parsers; } public synchronized void setT2Parsers(Set<T2Parser> parsers) { this.t2Parsers = parsers; } public boolean isStrict() { return strict; } protected void makeProfile(org.apache.taverna.scufl2.xml.t2flow.jaxb.Workflow wf) { // TODO: What should the default be? Should there be one? Who knows Profile profile = new Profile(wf.getProducedBy() == null ? DEFAULT_PRODUCED_BY : wf.getProducedBy()); profile.setParent(parserState.get().getCurrentWorkflowBundle()); parserState.get().getCurrentWorkflowBundle().setMainProfile(profile); parserState.get().setCurrentProfile(profile); } private URI makeRavenURI(Raven raven, String className) { if (raven == null) return ravenURI.resolve("undefined/" + uriTools.validFilename(className)); return ravenURI.resolve( uriTools.validFilename(raven.getGroup()) + "/" + uriTools.validFilename(raven.getArtifact()) + "/" + uriTools.validFilename(raven.getVersion()) + "/" + uriTools.validFilename(className)); } private URI mapTypeFromRaven(Raven raven, String activityClass) throws ReaderException { URI classURI = makeRavenURI(raven, activityClass); parserState.get().setCurrentT2Parser(null); T2Parser t2Parser = getT2Parser(classURI); if (t2Parser == null) { String message = "Unknown T2 activity or dispatch layer " + classURI + ", install supporting T2Parser"; if (isStrict()) throw new ReaderException(message); logger.warning(message); return classURI; } parserState.get().setCurrentT2Parser(t2Parser); return t2Parser.mapT2flowRavenIdToScufl2URI(classURI); } protected org.apache.taverna.scufl2.api.activity.Activity parseActivityAndAddToProfile(Activity origActivity) throws ReaderException { org.apache.taverna.scufl2.api.activity.Activity newActivity = parseActivity(origActivity); newActivity.setName(parserState.get().getCurrentProcessorBinding().getName()); parserState.get().getCurrentProfile().getActivities().addWithUniqueName(newActivity); newActivity.setParent(parserState.get().getCurrentProfile()); return newActivity; } protected org.apache.taverna.scufl2.api.activity.Activity parseActivity(Activity origActivity) throws ReaderException { Raven raven = origActivity.getRaven(); String activityClass = origActivity.getClazz(); URI activityId = mapTypeFromRaven(raven, activityClass); org.apache.taverna.scufl2.api.activity.Activity newActivity = new org.apache.taverna.scufl2.api.activity.Activity(); newActivity.setType(activityId); return newActivity; } protected void parseActivityBinding(Activity origActivity, int activityPosition) throws ReaderException, JAXBException { ProcessorBinding processorBinding = new ProcessorBinding(); processorBinding.setName(parserState.get().getCurrentProcessor().getName()); parserState.get().getCurrentProfile().getProcessorBindings().addWithUniqueName(processorBinding); processorBinding.setBoundProcessor(parserState.get().getCurrentProcessor()); parserState.get().setCurrentProcessorBinding(processorBinding); org.apache.taverna.scufl2.api.activity.Activity newActivity = parseActivityAndAddToProfile(origActivity); parserState.get().setCurrentActivity(newActivity); parserState.get().getCurrentProfile().getActivities().add(newActivity); processorBinding.setBoundActivity(newActivity); processorBinding.setActivityPosition(activityPosition); parserState.get().setCurrentConfigurable(newActivity); try { parseConfigurationAndAddToProfile(origActivity.getConfigBean()); } catch (JAXBException e) { if (isStrict()) throw e; logger.log(WARNING, "Can't configure activity" + newActivity, e); } parseActivityInputMap(origActivity.getInputMap()); parseActivityOutputMap(origActivity.getOutputMap()); parserState.get().setCurrentConfigurable(null); parserState.get().setCurrentActivity(null); parserState.get().setCurrentProcessorBinding(null); } protected Configuration parseConfigurationAndAddToProfile(ConfigBean configBean) throws JAXBException, ReaderException { Configuration configuration = parseConfiguration(configBean); if (configuration == null) return null; Profile profile = parserState.get().getCurrentProfile(); configuration.setName(parserState.get().getCurrentActivity().getName()); profile.getConfigurations().addWithUniqueName(configuration); configuration.setConfigures(parserState.get().getCurrentConfigurable()); return configuration; } protected Configuration parseConfiguration(ConfigBean configBean) throws JAXBException, ReaderException { // Placeholder to check later if no configuration have been provided Configuration UNCONFIGURED = new Configuration(); Configuration configuration = UNCONFIGURED; if (parserState.get().getCurrentT2Parser() == null) { String message = "No config parser for " + parserState.get().getCurrentConfigurable(); if (isStrict()) throw new ReaderException(message); return null; } try { configuration = parserState.get().getCurrentT2Parser().parseConfiguration(this, configBean, parserState.get()); } catch (ReaderException e) { if (isStrict()) throw e; } if (configuration == null) // Perfectly valid - true for say Invoke layer return null; if (configuration == UNCONFIGURED) { if (isStrict()) throw new ReaderException("No configuration returned from " + parserState.get().getCurrentT2Parser() + " for " + parserState.get().getCurrentConfigurable()); // We'll have to fall back by just keeping the existing XML configuration = new Configuration(); configuration.setType(configBeanURI.resolve("Config")); String xml = elementToXML((Element) configBean.getAny()); String encoding = configBean.getEncoding(); ObjectNode json = (ObjectNode) configuration.getJson(); json.put(encoding, xml); } return configuration; } public static String elementToXML(Element element) { try { Transformer transformer = getTransformer(); CharArrayWriter writer = new CharArrayWriter(); transformer.transform(new DOMSource(element), new StreamResult(writer)); return writer.toString(); } catch (TransformerException e) { throw new IllegalStateException("Can't write XML", e); } } public static Transformer getTransformer() throws TransformerConfigurationException { if (transformerFactory == null) transformerFactory = TransformerFactory.newInstance(); return transformerFactory.newTransformer(); } public Unmarshaller getUnmarshaller() { Unmarshaller u = unmarshaller.get(); if (!isValidating() && u.getSchema() != null) { u.setSchema(null); } else if (isValidating() && u.getSchema() == null) { // Load and set schema to validate against Schema schema; try { SchemaFactory schemaFactory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI); List<URI> schemas = getAdditionalSchemas(); URL t2flowExtendedXSD = T2FlowParser.class.getResource(T2FLOW_EXTENDED_XSD); schemas.add(t2flowExtendedXSD.toURI()); List<Source> schemaSources = new ArrayList<>(); for (URI schemaUri : schemas) schemaSources.add(new StreamSource(schemaUri.toASCIIString())); Source[] sources = schemaSources.toArray(new Source[schemaSources.size()]); schema = schemaFactory.newSchema(sources); } catch (SAXException e) { throw new RuntimeException("Can't load schemas", e); } catch (URISyntaxException | NullPointerException e) { throw new RuntimeException("Can't find schemas", e); } u.setSchema(schema); } return u; } protected List<URI> getAdditionalSchemas() { List<URI> uris = new ArrayList<>(); for (T2Parser parser : getT2Parsers()) { List<URI> schemas = parser.getAdditionalSchemas(); if (schemas != null) uris.addAll(schemas); } return uris; } protected void parseActivityInputMap(org.apache.taverna.scufl2.xml.t2flow.jaxb.Map inputMap) throws ReaderException { for (Mapping mapping : inputMap.getMap()) { String fromProcessorOutput = mapping.getFrom(); String toActivityOutput = mapping.getTo(); ProcessorInputPortBinding processorInputPortBinding = new ProcessorInputPortBinding(); InputProcessorPort inputProcessorPort = findNamed( parserState.get().getCurrentProcessor().getInputPorts(), fromProcessorOutput); if (inputProcessorPort == null) { String message = "Invalid input port binding, " + "unknown processor port: " + fromProcessorOutput + "->" + toActivityOutput + " in " + parserState.get().getCurrentProcessor(); if (isStrict()) throw new ReaderException(message); logger.warning(message); continue; } InputActivityPort inputActivityPort = parserState.get().getCurrentActivity().getInputPorts() .getByName(toActivityOutput); if (inputActivityPort == null) { inputActivityPort = new InputActivityPort(); inputActivityPort.setName(toActivityOutput); inputActivityPort.setParent(parserState.get().getCurrentActivity()); parserState.get().getCurrentActivity().getInputPorts().add(inputActivityPort); } if (inputActivityPort.getDepth() == null) inputActivityPort.setDepth(inputProcessorPort.getDepth()); processorInputPortBinding.setBoundActivityPort(inputActivityPort); processorInputPortBinding.setBoundProcessorPort(inputProcessorPort); parserState.get().getCurrentProcessorBinding().getInputPortBindings().add(processorInputPortBinding); } } protected void parseActivityOutputMap(org.apache.taverna.scufl2.xml.t2flow.jaxb.Map outputMap) throws ReaderException { for (Mapping mapping : outputMap.getMap()) { String fromActivityOutput = mapping.getFrom(); String toProcessorOutput = mapping.getTo(); ProcessorOutputPortBinding processorOutputPortBinding = new ProcessorOutputPortBinding(); OutputProcessorPort outputProcessorPort = findNamed( parserState.get().getCurrentProcessor().getOutputPorts(), toProcessorOutput); if (outputProcessorPort == null) { String message = "Invalid output port binding, " + "unknown processor port: " + fromActivityOutput + "->" + toProcessorOutput + " in " + parserState.get().getCurrentProcessor(); if (isStrict()) throw new ReaderException(message); logger.warning(message); continue; } OutputActivityPort outputActivityPort = parserState.get().getCurrentActivity().getOutputPorts() .getByName(fromActivityOutput); if (outputActivityPort == null) { outputActivityPort = new OutputActivityPort(); outputActivityPort.setName(fromActivityOutput); outputActivityPort.setParent(parserState.get().getCurrentActivity()); parserState.get().getCurrentActivity().getOutputPorts().add(outputActivityPort); } if (outputActivityPort.getDepth() == null) outputActivityPort.setDepth(outputProcessorPort.getDepth()); if (outputActivityPort.getGranularDepth() == null) outputActivityPort.setGranularDepth(outputProcessorPort.getGranularDepth()); processorOutputPortBinding.setBoundActivityPort(outputActivityPort); processorOutputPortBinding.setBoundProcessorPort(outputProcessorPort); parserState.get().getCurrentProcessorBinding().getOutputPortBindings().add(processorOutputPortBinding); } } protected Workflow parseDataflow(Dataflow df, Workflow wf) throws ReaderException, JAXBException { parserState.get().setCurrentWorkflow(wf); wf.setInputPorts(parseInputPorts(df.getInputPorts())); wf.setOutputPorts(parseOutputPorts(df.getOutputPorts())); wf.setProcessors(parseProcessors(df.getProcessors())); wf.setDataLinks(parseDatalinks(df.getDatalinks())); wf.setControlLinks(parseControlLinks(df.getConditions())); Revision revision = parseIdentificationAnnotations(df.getAnnotations()); if (revision != null) wf.setCurrentRevision(revision); parseAnnotations(wf, df.getAnnotations()); parserState.get().setCurrentWorkflow(null); return wf; } public void parseAnnotations(WorkflowBean annotatedBean, Annotations annotations) throws ReaderException { // logger.fine("Checking annotations for " + annotatedSubject); Map<String, NetSfTavernaT2AnnotationAnnotationAssertionImpl> annotationElems = new HashMap<>(); if (annotations == null || annotations.getAnnotationChainOrAnnotationChain22() == null) return; for (JAXBElement<AnnotationChain> el : annotations.getAnnotationChainOrAnnotationChain22()) { NetSfTavernaT2AnnotationAnnotationAssertionImpl ann = el.getValue() .getNetSfTavernaT2AnnotationAnnotationChainImpl().getAnnotationAssertions() .getNetSfTavernaT2AnnotationAnnotationAssertionImpl(); String annClass = ann.getAnnotationBean().getClazz(); if (annotationElems.containsKey(annClass) && annotationElems.get(annClass).getDate().compareToIgnoreCase(ann.getDate()) > 0) // ann.getDate() is less than current 'latest' annotation, skip continue; annotationElems.put(annClass, ann); } for (String clazz : annotationElems.keySet()) { NetSfTavernaT2AnnotationAnnotationAssertionImpl ann = annotationElems.get(clazz); Calendar cal = parseDate(ann.getDate()); String value = null; String semanticMediaType = TEXT_TURTLE; for (Object obj : ann.getAnnotationBean().getAny()) { if (!(obj instanceof Element)) continue; Element elem = (Element) obj; if (!(elem.getNamespaceURI() == null)) continue; if (elem.getLocalName().equals("text")) { value = elem.getTextContent().trim(); break; } if (clazz.equals(SEMANTIC_ANNOTATION)) { if (elem.getLocalName().equals("content")) value = elem.getTextContent().trim(); if (elem.getLocalName().equals("mimeType")) semanticMediaType = elem.getTextContent().trim(); } } if (value != null) { // Add the annotation Annotation annotation = new Annotation(); WorkflowBundle workflowBundle = parserState.get().getCurrentWorkflowBundle(); annotation.setParent(workflowBundle); String path = "annotation/" + annotation.getName() + ".ttl"; URI bodyURI = URI.create(path); annotation.setTarget(annotatedBean); annotation.setAnnotatedAt(cal); // annotation.setAnnotator(); annotation.setSerializedBy(t2flowParserURI); annotation.setSerializedAt(new GregorianCalendar()); URI annotatedSubject = uriTools.relativeUriForBean(annotatedBean, annotation); String body; if (clazz.equals(SEMANTIC_ANNOTATION)) { String baseStr = "@base <" + annotatedSubject.toASCIIString() + "> .\n"; body = baseStr + value; } else { // Generate Turtle from 'classic' annotation URI predicate = getPredicatesForClass().get(clazz); if (predicate == null) { if (isStrict()) throw new ReaderException("Unsupported annotation class " + clazz); return; } StringBuilder turtle = new StringBuilder(); turtle.append("<"); turtle.append(annotatedSubject.toASCIIString()); turtle.append("> "); turtle.append("<"); turtle.append(predicate.toASCIIString()); turtle.append("> "); // A potentially multi-line string turtle.append("\"\"\""); // Escape existing \ to \\ String escaped = value.replace("\\", "\\\\"); // Escape existing " to \" (beware Java's escaping of \ and " below) escaped = escaped.replace("\"", "\\\""); turtle.append(escaped); turtle.append("\"\"\""); turtle.append(" ."); body = turtle.toString(); } try { workflowBundle.getResources().addResource(body, path, semanticMediaType); } catch (IOException e) { throw new ReaderException("Could not store annotation body to " + path, e); } annotation.setBody(bodyURI); } } } private Map<String, URI> getPredicatesForClass() { if (this.predicates != null) return this.predicates; synchronized (this) { if (this.predicates != null) return this.predicates; Map<String, URI> predicates = new HashMap<>(); predicates.put("net.sf.taverna.t2.annotation.annotationbeans.DescriptiveTitle", URI.create("http://purl.org/dc/terms/title")); predicates.put("net.sf.taverna.t2.annotation.annotationbeans.Author", URI.create("http://purl.org/dc/elements/1.1/creator")); predicates.put("net.sf.taverna.t2.annotation.annotationbeans.FreeTextDescription", URI.create("http://purl.org/dc/terms/description")); predicates.put("net.sf.taverna.t2.annotation.annotationbeans.MimeType", URI.create("http://purl.org/dc/elements/1.1/format")); predicates.put("net.sf.taverna.t2.annotation.annotationbeans.ExampleValue", exampleDataURI); this.predicates = predicates; return this.predicates; } } protected Revision parseIdentificationAnnotations(Annotations annotations) { SortedMap<Calendar, UUID> revisions = new TreeMap<>(); if (annotations == null || annotations.getAnnotationChainOrAnnotationChain22() == null) return null; for (JAXBElement<AnnotationChain> el : annotations.getAnnotationChainOrAnnotationChain22()) { NetSfTavernaT2AnnotationAnnotationAssertionImpl ann = el.getValue() .getNetSfTavernaT2AnnotationAnnotationChainImpl().getAnnotationAssertions() .getNetSfTavernaT2AnnotationAnnotationAssertionImpl(); String annClass = ann.getAnnotationBean().getClazz(); if (!annClass.equals(IDENTIFICATION_ASSERTION)) continue; for (Object obj : ann.getAnnotationBean().getAny()) { if (!(obj instanceof Element)) continue; Element elem = (Element) obj; if (elem.getNamespaceURI() == null && elem.getLocalName().equals("identification")) { String uuid = elem.getTextContent().trim(); String date = ann.getDate(); Calendar cal = parseDate(date); revisions.put(cal, UUID.fromString(uuid)); } } } Revision rev = null; for (Entry<Calendar, UUID> entry : revisions.entrySet()) { Calendar cal = entry.getKey(); UUID uuid = entry.getValue(); URI uri = WORKFLOW_ROOT.resolve(uuid.toString() + "/"); rev = new Revision(uri, rev); rev.setGeneratedAtTime(cal); } return rev; } private Calendar parseDate(String dateStr) { // Based briefly on patterns used by // com.thoughtworks.xstream.converters.basic.DateConverter List<String> patterns = new ArrayList<>(); patterns.add("yyyy-MM-dd HH:mm:ss.S z"); patterns.add("yyyy-MM-dd HH:mm:ss z"); patterns.add("yyyy-MM-dd HH:mm:ssz"); patterns.add("yyyy-MM-dd HH:mm:ss.S 'UTC'"); patterns.add("yyyy-MM-dd HH:mm:ss 'UTC'"); Date date; for (String pattern : patterns) try { SimpleDateFormat dateFormat = new SimpleDateFormat(pattern, ENGLISH); date = dateFormat.parse(dateStr); GregorianCalendar cal = new GregorianCalendar(getTimeZone("UTC"), ENGLISH); cal.setTime(date); return cal; } catch (ParseException e) { continue; } throw new IllegalArgumentException("Can't parse date: " + dateStr); } private Set<ControlLink> parseControlLinks(Conditions conditions) throws ReaderException { Set<ControlLink> links = new HashSet<>(); for (Condition condition : conditions.getCondition()) { NamedSet<Processor> processors = parserState.get().getCurrentWorkflow().getProcessors(); String target = condition.getTarget(); Processor block = processors.getByName(target); if (block == null && isStrict()) throw new ReaderException("Unrecognized blocking processor in control link: " + target); String control = condition.getControl(); Processor untilFinished = processors.getByName(control); if (untilFinished == null && isStrict()) throw new ReaderException("Unrecognized untilFinished processor in control link: " + control); BlockingControlLink newLink = new BlockingControlLink(block, untilFinished); // FIXME: missing from t2flow and schema //parseAnnotations(newLink, condition.getAnnotations()); links.add(newLink); } return links; } protected Set<DataLink> parseDatalinks(Datalinks origLinks) throws ReaderException { HashSet<DataLink> newLinks = new HashSet<>(); Map<ReceiverPort, AtomicInteger> mergeCounts = new HashMap<>(); for (org.apache.taverna.scufl2.xml.t2flow.jaxb.DataLink origLink : origLinks.getDatalink()) { try { SenderPort senderPort = findSenderPort(parserState.get().getCurrentWorkflow(), origLink.getSource()); ReceiverPort receiverPort = findReceiverPort(parserState.get().getCurrentWorkflow(), origLink.getSink()); DataLink newLink = new DataLink(parserState.get().getCurrentWorkflow(), senderPort, receiverPort); AtomicInteger mergeCount = mergeCounts.get(receiverPort); if (origLink.getSink().getType().equals(MERGE)) { if (mergeCount != null && mergeCount.intValue() < 1) throw new ReaderException("Merged and non-merged links to port " + receiverPort); if (mergeCount == null) { mergeCount = new AtomicInteger(0); mergeCounts.put(receiverPort, mergeCount); } newLink.setMergePosition(mergeCount.getAndIncrement()); } else { if (mergeCount != null) throw new ReaderException("More than one link to non-merged port " + receiverPort); mergeCounts.put(receiverPort, new AtomicInteger(-1)); } // FIXME: missing from t2flow and schema //parseAnnotations(newLink, origLink.getAnnotations()); newLinks.add(newLink); } catch (ReaderException ex) { logger.log(WARNING, "Could not translate link:\n" + origLink, ex); if (isStrict()) throw ex; continue; } } return newLinks; } protected void parseDispatchStack(DispatchStack dispatchStack) throws ReaderException { Processor processor = parserState.get().getCurrentProcessor(); Configuration procConfig = scufl2Tools.createConfigurationFor(processor, parserState.get().getCurrentProfile()); parserState.get().setCurrentConfigurable(processor); parserState.get().setCurrentConfiguration(procConfig); parserState.get().setPreviousDispatchLayerName(null); try { for (DispatchLayer dispatchLayer : dispatchStack.getDispatchLayer()) parseDispatchStack(dispatchLayer); } finally { parserState.get().setCurrentConfigurable(null); parserState.get().setCurrentConfiguration(null); parserState.get().setPreviousDispatchLayerName(null); } } protected void parseDispatchStack(DispatchLayer dispatchLayer) throws ReaderException { URI typeUri = mapTypeFromRaven(dispatchLayer.getRaven(), dispatchLayer.getClazz()); ObjectNode procConfig = parserState.get().getCurrentConfiguration().getJsonAsObjectNode(); try { Configuration dispatchConfig = parseConfiguration(dispatchLayer.getConfigBean()); URI relUri = INTERNAL_DISPATCH_PREFIX.relativize(typeUri); String name; if (!relUri.isAbsolute()) { /* * It's an internal layer. We'll put it under the name which * we'll cleverly fish out of the URI path, eg. "retry" or * "parallelize" */ name = relUri.getPath().toLowerCase(); if (dispatchConfig != null && dispatchConfig.getJson().size() > 0) // But only if non-empty (non-default) procConfig.put(name, dispatchConfig.getJson()); } else { ObjectNode json; if (dispatchConfig != null && dispatchConfig.getJson().isObject()) json = dispatchConfig.getJsonAsObjectNode(); else { /* * We'll still need to create an objectNode to keep _type * and _below */ json = procConfig.objectNode(); if (dispatchConfig != null) // We'll put the non-objectnode here json.put("_config", dispatchConfig.getJson()); } /* * Not really much to go from here, we don't want to use the * typeUri as the name as third-party layers in theory could be * added several times for same type */ name = randomUUID().toString(); json.put("_type", typeUri.toString()); // Might be null - meaning "top" json.put("_below", parserState.get().getPreviousDispatchLayerName()); procConfig.put(name, json); } parserState.get().setPreviousDispatchLayerName(name); } catch (JAXBException ex) { String message = "Can't parse configuration for dispatch layer in " + parserState.get().getCurrentProcessor(); if (isStrict()) throw new ReaderException(message, ex); logger.log(WARNING, message, ex); } } protected Set<InputWorkflowPort> parseInputPorts(AnnotatedGranularDepthPorts originalPorts) throws ReaderException { Set<InputWorkflowPort> createdPorts = new HashSet<>(); for (AnnotatedGranularDepthPort originalPort : originalPorts.getPort()) { InputWorkflowPort newPort = new InputWorkflowPort(parserState.get().getCurrentWorkflow(), originalPort.getName()); newPort.setDepth(originalPort.getDepth().intValue()); if (!originalPort.getGranularDepth().equals(originalPort.getDepth())) { String message = "Specific input port granular depth not " + "supported in scufl2, port " + originalPort.getName() + " has depth " + originalPort.getDepth() + " and granular depth " + originalPort.getGranularDepth(); if (isStrict()) throw new ReaderException(message); logger.warning(message); } parseAnnotations(newPort, originalPort.getAnnotations()); createdPorts.add(newPort); } return createdPorts; } protected IterationStrategyStack parseIterationStrategyStack( org.apache.taverna.scufl2.xml.t2flow.jaxb.IterationStrategyStack originalStack) throws ReaderException { IterationStrategyStack newStack = new IterationStrategyStack(); for (TopIterationNode strategy : originalStack.getIteration().getStrategy()) { IterationNode topNode = strategy.getCross(); if (topNode == null) topNode = strategy.getDot(); if (topNode == null) continue; IterationNodeParent parent = (IterationNodeParent) topNode; if (parent.getCrossOrDotOrPort().isEmpty()) continue; try { newStack.add((IterationStrategyTopNode) parseIterationStrategyNode(topNode)); } catch (ReaderException e) { if (isStrict()) throw e; logger.warning(e.getMessage()); } } return newStack; } protected IterationStrategyNode parseIterationStrategyNode(IterationNode topNode) throws ReaderException { if (topNode instanceof PortProduct) { PortProduct portProduct = (PortProduct) topNode; PortNode portNode = new PortNode(); portNode.setDesiredDepth(portProduct.getDepth().intValue()); String name = portProduct.getName(); Processor processor = parserState.get().getCurrentProcessor(); InputProcessorPort inputProcessorPort = processor.getInputPorts().getByName(name); portNode.setInputProcessorPort(inputProcessorPort); return portNode; } IterationStrategyNode node; if (topNode instanceof DotProduct) node = new org.apache.taverna.scufl2.api.iterationstrategy.DotProduct(); else if (topNode instanceof CrossProduct) node = new org.apache.taverna.scufl2.api.iterationstrategy.CrossProduct(); else throw new ReaderException("Invalid node " + topNode); @SuppressWarnings("unchecked") List<IterationStrategyNode> children = (List<IterationStrategyNode>) node; IterationNodeParent parent = (IterationNodeParent) topNode; for (IterationNode child : parent.getCrossOrDotOrPort()) children.add(parseIterationStrategyNode(child)); return node; } protected Set<OutputWorkflowPort> parseOutputPorts(AnnotatedPorts originalPorts) throws ReaderException { Set<OutputWorkflowPort> createdPorts = new HashSet<>(); for (AnnotatedPort originalPort : originalPorts.getPort()) { OutputWorkflowPort newPort = new OutputWorkflowPort(parserState.get().getCurrentWorkflow(), originalPort.getName()); parseAnnotations(newPort, originalPort.getAnnotations()); createdPorts.add(newPort); } return createdPorts; } @SuppressWarnings("boxing") protected Set<InputProcessorPort> parseProcessorInputPorts(Processor newProc, DepthPorts origPorts) { Set<InputProcessorPort> newPorts = new HashSet<>(); for (DepthPort origPort : origPorts.getPort()) { InputProcessorPort newPort = new InputProcessorPort(newProc, origPort.getName()); newPort.setDepth(origPort.getDepth().intValue()); // TODO: What about InputProcessorPort granular depth? newPorts.add(newPort); } return newPorts; } @SuppressWarnings("boxing") protected Set<OutputProcessorPort> parseProcessorOutputPorts(Processor newProc, GranularDepthPorts origPorts) { Set<OutputProcessorPort> newPorts = new HashSet<>(); for (GranularDepthPort origPort : origPorts.getPort()) { OutputProcessorPort newPort = new OutputProcessorPort(newProc, origPort.getName()); newPort.setDepth(origPort.getDepth().intValue()); newPort.setGranularDepth(origPort.getGranularDepth().intValue()); newPorts.add(newPort); } return newPorts; } protected Set<Processor> parseProcessors(Processors originalProcessors) throws ReaderException, JAXBException { HashSet<Processor> newProcessors = new HashSet<>(); for (org.apache.taverna.scufl2.xml.t2flow.jaxb.Processor origProc : originalProcessors.getProcessor()) { Processor newProc = new Processor(parserState.get().getCurrentWorkflow(), origProc.getName()); parserState.get().setCurrentProcessor(newProc); newProc.setInputPorts(parseProcessorInputPorts(newProc, origProc.getInputPorts())); newProc.setOutputPorts(parseProcessorOutputPorts(newProc, origProc.getOutputPorts())); parseDispatchStack(origProc.getDispatchStack()); newProc.setIterationStrategyStack(parseIterationStrategyStack(origProc.getIterationStrategyStack())); parseAnnotations(newProc, origProc.getAnnotations()); newProcessors.add(newProc); int i = 0; for (Activity origActivity : origProc.getActivities().getActivity()) parseActivityBinding(origActivity, i++); } parserState.get().setCurrentProcessor(null); return newProcessors; } @SuppressWarnings("unchecked") public WorkflowBundle parseT2Flow(File t2File) throws IOException, ReaderException, JAXBException { JAXBElement<org.apache.taverna.scufl2.xml.t2flow.jaxb.Workflow> root = (JAXBElement<org.apache.taverna.scufl2.xml.t2flow.jaxb.Workflow>) getUnmarshaller() .unmarshal(t2File); return parseT2Flow(root.getValue()); } @SuppressWarnings("unchecked") public WorkflowBundle parseT2Flow(InputStream t2File) throws IOException, JAXBException, ReaderException { JAXBElement<org.apache.taverna.scufl2.xml.t2flow.jaxb.Workflow> root = (JAXBElement<org.apache.taverna.scufl2.xml.t2flow.jaxb.Workflow>) getUnmarshaller() .unmarshal(t2File); return parseT2Flow(root.getValue()); } public WorkflowBundle parseT2Flow(org.apache.taverna.scufl2.xml.t2flow.jaxb.Workflow wf) throws ReaderException, JAXBException { try { parserState.get().setT2FlowParser(this); WorkflowBundle wfBundle = new WorkflowBundle(); parserState.get().setCurrentWorkflowBundle(wfBundle); makeProfile(wf); // First a skeleton scan of workflows (for nested workflow configs) Map<Dataflow, Workflow> dataflowMap = new HashMap<>(); for (Dataflow df : wf.getDataflow()) { Workflow workflow = skeletonDataflow(df); dataflowMap.put(df, workflow); wfBundle.getWorkflows().addWithUniqueName(workflow); workflow.setParent(wfBundle); if (df.getRole().equals(TOP)) { wfBundle.setMainWorkflow(workflow); wfBundle.setName(df.getName()); wfBundle.setGlobalBaseURI(WORKFLOW_BUNDLE_ROOT.resolve(df.getId() + "/")); } } // Second stage for (Dataflow df : wf.getDataflow()) { Workflow workflow = dataflowMap.get(df); parseDataflow(df, workflow); } if (isStrict() && wfBundle.getMainWorkflow() == null) throw new ReaderException("No main workflow"); scufl2Tools.setParents(wfBundle); return wfBundle; } finally { parserState.remove(); } } protected Workflow skeletonDataflow(Dataflow df) { Workflow wf = new Workflow(); parserState.get().setCurrentWorkflow(wf); wf.setName(df.getName()); wf.setIdentifier(WORKFLOW_ROOT.resolve(df.getId() + "/")); return wf; } public void setStrict(boolean strict) { this.strict = strict; } }