org.apache.taverna.scufl2.api.io.structure.StructureReader.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.taverna.scufl2.api.io.structure.StructureReader.java

Source

package org.apache.taverna.scufl2.api.io.structure;

/*
 * 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.lang.System.arraycopy;
import static java.util.Collections.singleton;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Scanner;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.taverna.scufl2.api.activity.Activity;
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.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.io.WorkflowBundleReader;
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 com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * A <code>WorkflowBundleReader</code> that reads a {@link WorkflowBundle} in
 * Scufl2 Structure format.
 */
public class StructureReader implements WorkflowBundleReader {
    public enum Level {
        WorkflowBundle, Workflow, Processor, Activity, Links, Profile, Configuration, ProcessorBinding, OutputPortBindings, InputPortBindings, JSON, Controls
    }

    private static final String ACTIVITY_SLASH = "activity/";
    public static final String TEXT_VND_TAVERNA_SCUFL2_STRUCTURE = "text/vnd.taverna.scufl2.structure";

    private WorkflowBundle wb;
    private Level level;
    private String mainWorkflow;
    private Workflow workflow;
    private Processor processor;
    private Activity activity;
    Pattern linkPattern = Pattern.compile("'(.*[^\\\\])'\\s->\\s'(.*[^\\\\\\\\])'");
    Pattern blockPattern = Pattern.compile("\\s*block\\s+'(.*[^\\\\])'\\s+until\\s+'(.*[^\\\\\\\\])'\\s+finish");
    private String mainProfile;
    private Profile profile;
    private Configuration configuration;
    private ProcessorBinding processorBinding;
    protected Scanner scanner;

    @Override
    public Set<String> getMediaTypes() {
        return singleton(TEXT_VND_TAVERNA_SCUFL2_STRUCTURE);
    }

    // synchronized because we share wb/scanner fields across the instance
    protected synchronized WorkflowBundle parse(InputStream is) throws ReaderException {
        scanner = new Scanner(is);
        try {
            wb = new WorkflowBundle();
            while (scanner.hasNextLine())
                parseLine(scanner.nextLine());
            return wb;
        } finally {
            scanner.close();
        }
    }

    protected void parseLine(final String nextLine) throws ReaderException {
        try (Scanner scanner = new Scanner(nextLine.trim())) {
            // In case it is the last line
            if (!scanner.hasNext())
                return;
            // allow any whitespace
            String next = scanner.next();

            if (next.isEmpty())
                return;
            switch (next) {
            case "WorkflowBundle":
                parseWorkflowBundle(scanner);
                return;
            case "MainWorkflow":
                mainWorkflow = parseName(scanner);
                return;
            case "Workflow":
                parseWorkflow(scanner);
                return;
            case "In":
            case "Out":
                parsePort(scanner, next);
                return;
            case "Links":
                level = Level.Links;
                processor = null;
                return;
            case "Controls":
                level = Level.Controls;
                return;
            case "MainProfile":
                mainProfile = parseName(scanner);
                return;
            case "Profile":
                parseProfile(scanner);
                return;
            case "Type":
                parseType(nextLine);
                return;
            case "ProcessorBinding":
                parseProcessorBinding(scanner);
                return;
            case "InputPortBindings":
                level = Level.InputPortBindings;
                return;
            case "OutputPortBindings":
                level = Level.OutputPortBindings;
                return;
            case "Configuration":
                parseConfiguration(scanner);
                return;
            case "Configures":
                parseConfigures(scanner);
                return;
            case "Activity":
                switch (level) {
                case Profile:
                case Activity:
                    level = Level.Activity;
                    activity = new Activity();
                    activity.setName(parseName(scanner));
                    profile.getActivities().add(activity);
                    return;
                case ProcessorBinding:
                    Activity boundActivity = profile.getActivities().getByName(parseName(scanner));
                    processorBinding.setBoundActivity(boundActivity);
                    return;
                default:
                    break;
                }
                break;
            case "Processor":
                switch (level) {
                case Workflow:
                case Processor:
                    level = Level.Processor;
                    processor = new Processor();
                    processor.setName(parseName(scanner));
                    processor.setParent(workflow);
                    workflow.getProcessors().add(processor);
                    return;
                case ProcessorBinding:
                    String[] wfProcName = parseName(scanner).split(":");
                    Workflow wf = wb.getWorkflows().getByName(wfProcName[0]);
                    Processor boundProcessor = wf.getProcessors().getByName(wfProcName[1]);
                    processorBinding.setBoundProcessor(boundProcessor);
                    return;
                default:
                    break;
                }
                break;
            }

            if (next.equals("block")) {
                Matcher blockMatcher = blockPattern.matcher(nextLine);
                blockMatcher.find();
                String block = blockMatcher.group(1);
                String untilFinish = blockMatcher.group(2);

                Processor blockProc = workflow.getProcessors().getByName(block);
                Processor untilFinishedProc = workflow.getProcessors().getByName(untilFinish);
                new BlockingControlLink(blockProc, untilFinishedProc);
            }
            if (next.startsWith("'") && level.equals(Level.Links)) {
                Matcher linkMatcher = linkPattern.matcher(nextLine);
                linkMatcher.find();
                String firstLink = linkMatcher.group(1);
                String secondLink = linkMatcher.group(2);

                SenderPort senderPort;
                if (firstLink.contains(":")) {
                    String[] procPort = firstLink.split(":");
                    Processor proc = workflow.getProcessors().getByName(procPort[0]);
                    senderPort = proc.getOutputPorts().getByName(procPort[1]);
                } else
                    senderPort = workflow.getInputPorts().getByName(firstLink);

                ReceiverPort receiverPort;
                if (secondLink.contains(":")) {
                    String[] procPort = secondLink.split(":");
                    Processor proc = workflow.getProcessors().getByName(procPort[0]);
                    receiverPort = proc.getInputPorts().getByName(procPort[1]);
                } else
                    receiverPort = workflow.getOutputPorts().getByName(secondLink);

                new DataLink(workflow, senderPort, receiverPort);
                return;
            }

            if (next.startsWith("'") && (level == Level.InputPortBindings || level == Level.OutputPortBindings)) {
                Matcher linkMatcher = linkPattern.matcher(nextLine);
                linkMatcher.find();
                String firstLink = linkMatcher.group(1);
                String secondLink = linkMatcher.group(2);
                if (level == Level.InputPortBindings) {
                    InputProcessorPort processorPort = processorBinding.getBoundProcessor().getInputPorts()
                            .getByName(firstLink);
                    InputActivityPort activityPort = processorBinding.getBoundActivity().getInputPorts()
                            .getByName(secondLink);
                    new ProcessorInputPortBinding(processorBinding, processorPort, activityPort);
                } else {
                    OutputActivityPort activityPort = processorBinding.getBoundActivity().getOutputPorts()
                            .getByName(firstLink);
                    OutputProcessorPort processorPort = processorBinding.getBoundProcessor().getOutputPorts()
                            .getByName(secondLink);
                    new ProcessorOutputPortBinding(processorBinding, activityPort, processorPort);
                }
                return;
            }
            if (level == Level.JSON) {
                /*
                 * A silly reader that feeds (no more than) a single line at a
                 * time from our parent scanner, starting with the current line
                 */
                Reader reader = new Reader() {
                    char[] line = nextLine.toCharArray();
                    int pos = 0;

                    @Override
                    public int read(char[] cbuf, int off, int len) throws IOException {
                        if (pos >= line.length) {
                            // Need to read next line to fill buffer
                            if (!StructureReader.this.scanner.hasNextLine())
                                return -1;
                            String newLine = StructureReader.this.scanner.nextLine();
                            pos = 0;
                            line = newLine.toCharArray();
                            // System.out.println("Read new line: " + newLine);
                        }
                        int length = Math.min(len, line.length - pos);
                        if (length <= 0)
                            return 0;
                        arraycopy(line, pos, cbuf, off, length);
                        pos += length;
                        return length;
                    }

                    @Override
                    public void close() throws IOException {
                        line = null;
                    }
                };

                ObjectMapper mapper = new ObjectMapper();
                try {
                    JsonParser parser = mapper.getFactory().createParser(reader);
                    JsonNode jsonNode = parser.readValueAs(JsonNode.class);
                    // System.out.println("Parsed " + jsonNode);
                    configuration.setJson(jsonNode);
                } catch (IOException e) {
                    throw new ReaderException("Can't parse json", e);
                }
                level = Level.Configuration;
                return;
            }
        }
    }

    private void parseWorkflowBundle(Scanner scanner) {
        level = Level.WorkflowBundle;
        String name = parseName(scanner);
        wb.setName(name);
    }

    private void parseWorkflow(Scanner scanner) {
        level = Level.Workflow;
        workflow = new Workflow();
        String workflowName = parseName(scanner);
        workflow.setName(workflowName);
        wb.getWorkflows().add(workflow);
        if (workflowName.equals(mainWorkflow))
            wb.setMainWorkflow(workflow);
    }

    private void parsePort(Scanner scanner, String next) throws ReaderException {
        boolean in = next.equals("In");
        String portName = parseName(scanner);
        switch (level) {
        case Workflow:
            if (in)
                new InputWorkflowPort(workflow, portName);
            else
                new OutputWorkflowPort(workflow, portName);
            break;
        case Processor:
            if (in)
                new InputProcessorPort(processor, portName);
            else
                new OutputProcessorPort(processor, portName);
            break;
        case Activity:
            if (in)
                new InputActivityPort(activity, portName);
            else
                new OutputActivityPort(activity, portName);
            break;
        default:
            throw new ReaderException("Unexpected " + next + " at level " + level);
        }
    }

    private void parseProfile(Scanner scanner) {
        level = Level.Profile;
        profile = new Profile();
        String profileName = parseName(scanner);
        profile.setName(profileName);
        wb.getProfiles().add(profile);
        if (profileName.equals(mainProfile))
            wb.setMainProfile(profile);
    }

    private void parseType(String nextLine) {
        URI uri = URI.create(nextLine.split("[<>]")[1]);
        switch (level) {
        case Activity:
            activity.setType(uri);
            break;
        case Configuration:
            configuration.setType(uri);
            break;
        default:
            break;
        }
    }

    private void parseProcessorBinding(Scanner scanner) {
        level = Level.ProcessorBinding;
        processorBinding = new ProcessorBinding();
        String bindingName = parseName(scanner);
        processorBinding.setName(bindingName);
        profile.getProcessorBindings().add(processorBinding);
    }

    private void parseConfiguration(Scanner scanner) {
        level = Level.Configuration;
        configuration = new Configuration();
        String configName = parseName(scanner);
        configuration.setName(configName);
        profile.getConfigurations().add(configuration);
    }

    private void parseConfigures(Scanner scanner) {
        String configures = parseName(scanner);
        if (!configures.startsWith(ACTIVITY_SLASH))
            throw new UnsupportedOperationException("Unknown Configures " + configures);
        activity = profile.getActivities()
                .getByName(configures.substring(ACTIVITY_SLASH.length(), configures.length()));
        configuration.setConfigures(activity);
        level = Level.JSON;
    }

    private String parseName(Scanner scanner) {
        String name = scanner.findInLine("'(.*[^\\\\])'");
        return name.substring(1, name.length() - 1);
    }

    @Override
    public WorkflowBundle readBundle(File bundleFile, String mediaType) throws IOException, ReaderException {
        try (BufferedInputStream is = new BufferedInputStream(new FileInputStream(bundleFile))) {
            return parse(is);
        }
    }

    @Override
    public WorkflowBundle readBundle(InputStream inputStream, String mediaType)
            throws IOException, ReaderException {
        return parse(inputStream);
    }

    @Override
    public String guessMediaTypeForSignature(byte[] firstBytes) {
        if (new String(firstBytes, Charset.forName("ISO-8859-1")).contains("WorkflowBundle '"))
            return TEXT_VND_TAVERNA_SCUFL2_STRUCTURE;
        return null;
    }
}