All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.okapi.common.pipeline.Pipeline Maven / Gradle / Ivy

There is a newer version: 1.46.0
Show newest version
/*===========================================================================
  Copyright (C) 2008-2011 by the Okapi Framework contributors
-----------------------------------------------------------------------------
  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 net.sf.okapi.common.pipeline;

import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;

import net.sf.okapi.common.Event;
import net.sf.okapi.common.EventType;
import net.sf.okapi.common.observer.BaseObservable;
import net.sf.okapi.common.observer.IObservable;
import net.sf.okapi.common.observer.IObserver;
import net.sf.okapi.common.resource.MultiEvent;
import net.sf.okapi.common.resource.RawDocument;

/**
 * Default implementations of the {@link IPipeline} interface.
 */
public class Pipeline implements IPipeline, IObservable, IObserver {
	public static final String DEFAULT_ID = "DEFAULT ID";

	private LinkedList steps;
	private LinkedList finishedSteps;
	private volatile PipelineReturnValue state;
	private String id;

	private boolean notifiedObserver;

	/**
	 * Creates a new Pipeline object.
	 */
	public Pipeline() {
		steps = new LinkedList<>();
		finishedSteps = new LinkedList<>();
		state = PipelineReturnValue.PAUSED;
		id = DEFAULT_ID;
	}

	private void initialize() {
		// Copy all the finished steps from previous run
		steps.addAll(finishedSteps);
		finishedSteps.clear();
	}

	@Override
	public void startBatch() {
		state = PipelineReturnValue.RUNNING;

		initialize();

		Event event = new Event(EventType.START_BATCH);
		for (IPipelineStep step : steps) {
			step.handleEvent(event);
		}
		notifyObservers(event);
	}

	@Override
	public void endBatch() {
		// Non-terminal steps will return END_BATCH after receiving END_BATCH
		// Terminal steps return an event which may be anything,
		// including a CUSTOM event. The pipeline returns this final event.
		// We run this on finishedSteps since steps is empty by the time we get
		// here
		Event event = Event.createEndBatchEvent();
		for (IPipelineStep step : finishedSteps) {
			step.handleEvent(event);
		}
		notifyObservers(event);

		state = PipelineReturnValue.SUCCEDED;
	}

	@Override
	public void addStep(IPipelineStep step) {
		steps.add(step);
	}

	@Override
	public List getSteps() {
		return new LinkedList<>(steps);
	}

	@Override
	public void cancel() {
		state = PipelineReturnValue.CANCELLED;
	}

	private Event execute(Event event) {
		notifiedObserver = false;
		state = PipelineReturnValue.RUNNING;
		
		// loop through the events until we run out of steps or hit cancel
		while (!steps.isEmpty() && !(state == PipelineReturnValue.CANCELLED)) {
			// cycle through the steps in order, pulling off steps that run out
			// of events.
			do {
				// go to each active step and call handleEvent
				// the event returned is used as input to the next pass
				notifiedObserver = false;
				for (IPipelineStep step : steps) {
					event = step.handleEvent(event);
					// Recursively expand the event if needed
					event = expandEvent(event, step);
					if (event.isNoop()) break; // The event has been processed in expandEvent(), no need to process further here
				}

				// notify observers that the final step has sent an Event
				if (!notifiedObserver && !event.isNoop()) {
					notifyObservers(event);
				}
				
				// If the first step is not done yet:
				// Reset the event, otherwise the first step can get a RAW_DOCUMENT
				// or other events generated by the last step even if it is still not done.
				if ( !steps.getFirst().isDone() ) {
					event = Event.createNoopEvent();
				}
				
			} while (!steps.getFirst().isDone() && !(state == PipelineReturnValue.CANCELLED));
			// As each step exhausts its events remove it from the list and move
			// on to the next
			try {
				while (steps.getFirst().isDone()) {
					finishedSteps.add(steps.remove());
				}
			} catch (NoSuchElementException e) {
				// ignore
			}
		}
		
		// Cancel steps if the pipeline is canceled
		if (state == PipelineReturnValue.CANCELLED) {
			for (IPipelineStep step : steps) {
				step.cancel();
			}
		}

		return event;
	}

	/*
	 * Test the event and if it is a MULTI_EVENT then expand it and process each contained Event.
	 * We must do this recursively.
	 */
	private Event expandEvent(Event event, IPipelineStep currentStep) {
		// We send each of the events in MULTI_EVENT down the pipeline before
		// processing any other events but only if the event is configured for multi-event propagation
		if (event.getEventType() == EventType.MULTI_EVENT
				&& !(((MultiEvent) event.getResource()).isPropagateAsSingleEvent())) {

			// add the remaining steps to a temp list - these are the steps that will receive the expanded
			// MULT_EVENTS
			List remainingSteps = steps.subList(steps.indexOf(currentStep) + 1,
					steps.size());
			for (Event me : ((MultiEvent)event.getResource())) {
				event = me;
				// send the current event from MULTI_EVENT down the remaining steps in the pipeline
				for (IPipelineStep remainingStep : remainingSteps) {
					event = remainingStep.handleEvent(event);
					event = expandEvent(event, remainingStep);
					if (event.isNoop()) break; // The event has been processed in expandEvent(), no need to process further here
				}
				// notify observers that the final step has sent an Event
				// always filter out NO_OP events
				if (!event.isNoop()) {
					notifyObservers(event);
					notifiedObserver = true;
				}				
			}
			return Event.createNoopEvent(); // All events have been propagated and processed by remaining steps 
		}
		
		return event;
	}

	@Override
	public PipelineReturnValue getState() {
		return state;
	}

	@Override
	public void  process(RawDocument input) {
		process(new Event(EventType.RAW_DOCUMENT, input));
	}

	@Override
	public void  process(Event input) {		
		state = PipelineReturnValue.RUNNING;
		initialize();

		// Pre-process for this batch-item
		Event e = new Event(EventType.START_BATCH_ITEM);
		for (IPipelineStep step : steps) {
			e = step.handleEvent(e);
		}
		notifyObservers(e);

		// Prime the pipeline with the input Event and run it to completion.		
		// catch case where the first event is MULTI_EVENT
		if (input.getEventType() == EventType.MULTI_EVENT && 
				!(((MultiEvent)input.getResource()).isPropagateAsSingleEvent())) {
			for (Event me : ((MultiEvent) input.getResource())) {
				execute(me);
				// Copy any remaining steps into finishedSteps - makes initialization
				// process easier down the road if we use the pipeline again
				finishedSteps.addAll(steps);
				steps.clear();
				initialize();
			}
		} else {
			execute(input);
		}

		// Copy any remaining steps into finishedSteps - makes initialization
		// process easier down the road if we use the pipeline again
		finishedSteps.addAll(steps);
		steps.clear();

		// Post-process for this batch-item
		e = new Event(EventType.END_BATCH_ITEM);
		for (IPipelineStep step : finishedSteps) {
			e = step.handleEvent(e);
		}
		notifyObservers(e);
	}

	@Override
	public void destroy() {
		for (IPipelineStep step : finishedSteps) {
			step.destroy();
		}
		state = PipelineReturnValue.DESTROYED;
	}

	@Override
	public void clearSteps() {
		destroy();
		steps.clear();
		finishedSteps.clear();
	}

	@Override
	public String getId() {
		return id;
	}

	@Override
	public void setId(String id) {
		this.id = id;
	}

	//
	// implements IObserver interface
	//

	public void update(IObservable o, Object arg) {
		notifyObservers();
	}

	//
	// implements IObservable interface
	//

	/**
	 * Implements multiple inheritance via delegate pattern to an inner class
	 * 
	 * @see IObservable
	 * @see BaseObservable
	 */
	private IObservable delegatedObservable = new BaseObservable(this);

	public void addObserver(IObserver observer) {
		delegatedObservable.addObserver(observer);
	}

	public int countObservers() {
		return delegatedObservable.countObservers();
	}

	public void deleteObserver(IObserver observer) {
		delegatedObservable.deleteObserver(observer);
	}

	public void notifyObservers() {
		delegatedObservable.notifyObservers();
	}

	public void notifyObservers(Object arg) {
		delegatedObservable.notifyObservers(arg);
	}

	public void deleteObservers() {
		delegatedObservable.deleteObservers();
	}

	public List getObservers() {
		return delegatedObservable.getObservers();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy