917 lines
26 KiB
Java
917 lines
26 KiB
Java
/*
|
|
* Copyright 2015 Martin Smock <martin.smock@bluewin.ch>
|
|
*
|
|
* 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 li.strolch.model.activity;
|
|
|
|
import li.strolch.exception.StrolchElementNotFoundException;
|
|
import li.strolch.exception.StrolchException;
|
|
import li.strolch.exception.StrolchModelException;
|
|
import li.strolch.exception.StrolchPolicyException;
|
|
import li.strolch.model.*;
|
|
import li.strolch.model.Locator.LocatorBuilder;
|
|
import li.strolch.model.parameter.Parameter;
|
|
import li.strolch.model.policy.PolicyDef;
|
|
import li.strolch.model.policy.PolicyDefs;
|
|
import li.strolch.model.visitor.StrolchElementVisitor;
|
|
import li.strolch.model.xml.StrolchXmlHelper;
|
|
import li.strolch.utils.dbc.DBC;
|
|
|
|
import java.text.MessageFormat;
|
|
import java.util.*;
|
|
import java.util.Map.Entry;
|
|
import java.util.function.Predicate;
|
|
import java.util.function.Supplier;
|
|
import java.util.stream.Stream;
|
|
|
|
import static java.util.stream.Collectors.toList;
|
|
import static li.strolch.model.StrolchModelConstants.BAG_PARAMETERS;
|
|
import static li.strolch.model.StrolchModelConstants.BAG_RELATIONS;
|
|
import static li.strolch.model.StrolchModelConstants.PolicyConstants.BAG_OBJECTIVES;
|
|
import static li.strolch.utils.collections.CollectionsHelper.singletonCollector;
|
|
|
|
/**
|
|
* Parameterized object grouping a collection of {@link Activity} and {@link Action} objects defining the process to be
|
|
* scheduled
|
|
*
|
|
* @author Martin Smock <martin.smock@bluewin.ch>
|
|
*/
|
|
public class Activity extends AbstractStrolchRootElement
|
|
implements IActivityElement, StrolchRootElement, Comparable<Activity> {
|
|
|
|
protected Locator locator;
|
|
protected Version version;
|
|
|
|
protected Activity parent;
|
|
protected TimeOrdering timeOrdering;
|
|
protected Map<String, IActivityElement> elements;
|
|
protected PolicyDefs policyDefs;
|
|
|
|
/**
|
|
* Empty constructor - for marshalling only!
|
|
*/
|
|
public Activity() {
|
|
super();
|
|
}
|
|
|
|
/**
|
|
* Default constructor
|
|
*
|
|
* @param id the id
|
|
* @param name the name
|
|
* @param type the type
|
|
*/
|
|
public Activity(String id, String name, String type, TimeOrdering timeOrdering) {
|
|
super(id, name, type);
|
|
this.timeOrdering = timeOrdering;
|
|
}
|
|
|
|
@Override
|
|
public void setId(String id) {
|
|
this.locator = null;
|
|
super.setId(id);
|
|
}
|
|
|
|
@Override
|
|
public String getObjectType() {
|
|
return Tags.ACTIVITY;
|
|
}
|
|
|
|
public TimeOrdering getTimeOrdering() {
|
|
return this.timeOrdering;
|
|
}
|
|
|
|
public void setTimeOrdering(TimeOrdering timeOrdering) {
|
|
assertNotReadonly();
|
|
this.timeOrdering = timeOrdering;
|
|
}
|
|
|
|
@Override
|
|
public boolean hasVersion() {
|
|
return this.version != null;
|
|
}
|
|
|
|
@Override
|
|
public Version getVersion() {
|
|
return this.version;
|
|
}
|
|
|
|
@Override
|
|
public void setVersion(Version version) throws IllegalArgumentException, IllegalStateException {
|
|
if (!isRootElement())
|
|
throw new IllegalStateException("Can't set the version on non root of " + getLocator());
|
|
|
|
if (version != null && !getLocator().equals(version.getLocator())) {
|
|
String msg = "Illegal version as locator is not same: Element: {0} Version: {1}";
|
|
throw new IllegalArgumentException(MessageFormat.format(msg, getLocator(), version));
|
|
}
|
|
|
|
this.version = version;
|
|
}
|
|
|
|
private void initElements() {
|
|
if (this.elements == null) {
|
|
// use a LinkedHashMap since we will iterate elements in the order
|
|
// added and lookup elements by ID
|
|
this.elements = new LinkedHashMap<>();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isAction() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isActivity() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns true if this {@link Activity} contains any children i.e. any of {@link Action} or {@link Activity}
|
|
*
|
|
* @return true if this {@link Activity} contains any children i.e. any of {@link Action} or {@link Activity}
|
|
*/
|
|
public boolean hasElements() {
|
|
return this.elements != null && !this.elements.isEmpty();
|
|
}
|
|
|
|
/**
|
|
* Returns true if this {@link Activity} contains a child with the given id. The element instance type is ignored,
|
|
* i.e. {@link Action} or {@link Activity}
|
|
*
|
|
* @param id the id of the element to check for
|
|
*
|
|
* @return true if this {@link Activity} contains a child with the given id. The element instance type is ignored,
|
|
* i.e. {@link Action} or {@link Activity}
|
|
*/
|
|
public boolean hasElement(String id) {
|
|
return this.elements != null && this.elements.containsKey(id);
|
|
}
|
|
|
|
/**
|
|
* add an activity element to the {@code LinkedHashMap} of {@code IActivityElements}
|
|
*
|
|
* @param activityElement the element to add
|
|
*/
|
|
public void addElement(IActivityElement activityElement) {
|
|
assertCanAdd(activityElement);
|
|
activityElement.setParent(this);
|
|
this.elements.put(activityElement.getId(), activityElement);
|
|
}
|
|
|
|
/**
|
|
* add an activity element to the {@code LinkedHashMap} of {@code IActivityElements} before the given element
|
|
*
|
|
* @param elementId the id of the element before which to add the other element
|
|
* @param elementToAdd the element to add
|
|
*/
|
|
public void addElementBefore(String elementId, IActivityElement elementToAdd) {
|
|
IActivityElement element = getElement(elementId);
|
|
addElementBefore(element, elementToAdd);
|
|
}
|
|
|
|
/**
|
|
* add an activity element to the {@code LinkedHashMap} of {@code IActivityElements} before the given element
|
|
*
|
|
* @param element the element before which to add the other element
|
|
* @param elementToAdd the element to add
|
|
*/
|
|
public void addElementBefore(IActivityElement element, IActivityElement elementToAdd) {
|
|
assertCanAdd(elementToAdd);
|
|
|
|
Iterator<Entry<String, IActivityElement>> iterator = this.elements.entrySet().iterator();
|
|
LinkedHashMap<String, IActivityElement> elements = new LinkedHashMap<>();
|
|
boolean added = false;
|
|
while (iterator.hasNext()) {
|
|
Entry<String, IActivityElement> next = iterator.next();
|
|
|
|
if (!added && next.getValue().equals(element)) {
|
|
elements.put(elementToAdd.getId(), elementToAdd);
|
|
elements.put(next.getKey(), next.getValue());
|
|
added = true;
|
|
}
|
|
|
|
elements.put(next.getKey(), next.getValue());
|
|
}
|
|
if (!added)
|
|
throw new IllegalStateException("Element " + element.getId() + " was not found, couldn't add before!");
|
|
|
|
elementToAdd.setParent(this);
|
|
this.elements = elements;
|
|
}
|
|
|
|
/**
|
|
* add an activity element to the {@code LinkedHashMap} of {@code IActivityElements} after the given element
|
|
*
|
|
* @param elementId the id of the element after which to add the other element
|
|
* @param elementToAdd the element to add
|
|
*/
|
|
public void addElementAfter(String elementId, IActivityElement elementToAdd) {
|
|
IActivityElement element = getElement(elementId);
|
|
addElementAfter(element, elementToAdd);
|
|
}
|
|
|
|
/**
|
|
* add an activity element to the {@code LinkedHashMap} of {@code IActivityElements} after the given element
|
|
*
|
|
* @param element the element after which to add the other element
|
|
* @param elementToAdd the element to add
|
|
*/
|
|
public void addElementAfter(IActivityElement element, IActivityElement elementToAdd) {
|
|
assertCanAdd(elementToAdd);
|
|
|
|
Iterator<Entry<String, IActivityElement>> iterator = this.elements.entrySet().iterator();
|
|
LinkedHashMap<String, IActivityElement> elements = new LinkedHashMap<>();
|
|
boolean added = false;
|
|
while (iterator.hasNext()) {
|
|
Entry<String, IActivityElement> next = iterator.next();
|
|
elements.put(next.getKey(), next.getValue());
|
|
|
|
if (!added && next.getValue().equals(element)) {
|
|
elements.put(elementToAdd.getId(), elementToAdd);
|
|
added = true;
|
|
}
|
|
}
|
|
if (!added)
|
|
throw new IllegalStateException("Element " + element.getId() + " was not found, couldn't add after!");
|
|
|
|
elementToAdd.setParent(this);
|
|
this.elements = elements;
|
|
}
|
|
|
|
private void assertCanAdd(IActivityElement elementToAdd) {
|
|
assertNotReadonly();
|
|
DBC.PRE.assertNotEquals("Can't add element to itself!", this, elementToAdd);
|
|
DBC.PRE.assertNull("Parent can't already be set!", elementToAdd.getParent());
|
|
|
|
// TODO make sure we can't create a circular dependency
|
|
|
|
initElements();
|
|
String id = elementToAdd.getId();
|
|
if (id == null)
|
|
throw new StrolchException("Cannot add IActivityElement without id.");
|
|
if (this.elements.containsKey(id))
|
|
throw new StrolchException(
|
|
"Activity " + getLocator() + " already contains an activity element with id = " + id);
|
|
}
|
|
|
|
/**
|
|
* Removes the element with the given id and returns it, if it exists
|
|
*
|
|
* @param id the id of the element to remove
|
|
*
|
|
* @return the removed element, or null if it does not exist
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public <T extends IActivityElement> T remove(String id) {
|
|
assertNotReadonly();
|
|
IActivityElement element = this.elements.remove(id);
|
|
if (element != null)
|
|
element.setParent(null);
|
|
return (T) element;
|
|
}
|
|
|
|
/**
|
|
* Returns the {@link Action} with the given ID which is a direct child of this {@link Activity}
|
|
*
|
|
* @param id the id of the {@link Action} to return
|
|
*
|
|
* @return the {@link Action} with the given ID
|
|
*/
|
|
public Action getAction(String id) {
|
|
return getElement(id);
|
|
}
|
|
|
|
/**
|
|
* Returns the {@link Activity} with the given ID which is a direct child of this {@link Activity}
|
|
*
|
|
* @param id the id of the {@link Activity} to return
|
|
*
|
|
* @return the {@link Activity} with the given ID
|
|
*/
|
|
public Activity getActivity(String id) {
|
|
return getElement(id);
|
|
}
|
|
|
|
/**
|
|
* get {@code IActivityElement} by id
|
|
*
|
|
* @param id the id of the {@code IActivityElement}
|
|
*
|
|
* @return IActivityElement
|
|
*/
|
|
public <T extends IActivityElement> T getElement(String id) {
|
|
if (this.elements == null)
|
|
throw new IllegalArgumentException("Element " + id + " does not exist on " + getLocator());
|
|
@SuppressWarnings("unchecked") T t = (T) this.elements.get(id);
|
|
if (t == null)
|
|
throw new IllegalArgumentException("Element " + id + " does not exist on " + getLocator());
|
|
return t;
|
|
}
|
|
|
|
public Optional<IActivityElement> getPreviousElement(IActivityElement element) {
|
|
if (!hasElements())
|
|
return Optional.empty();
|
|
|
|
IActivityElement previous = null;
|
|
|
|
Iterator<Entry<String, IActivityElement>> iter = elementIterator();
|
|
while (iter.hasNext()) {
|
|
IActivityElement elem = iter.next().getValue();
|
|
if (elem == element)
|
|
return Optional.ofNullable(previous);
|
|
else
|
|
previous = elem;
|
|
}
|
|
|
|
return Optional.empty();
|
|
}
|
|
|
|
public Optional<IActivityElement> getPreviousElementByType(IActivityElement element, String type) {
|
|
if (!hasElements())
|
|
return Optional.empty();
|
|
|
|
List<IActivityElement> reversed = new ArrayList<>(this.elements.values());
|
|
Collections.reverse(reversed);
|
|
|
|
boolean foundElem = false;
|
|
Iterator<IActivityElement> iter = reversed.iterator();
|
|
IActivityElement elem;
|
|
while (iter.hasNext()) {
|
|
elem = iter.next();
|
|
|
|
if (foundElem && elem.getType().equals(type))
|
|
return Optional.of(elem);
|
|
else if (elem == element) {
|
|
foundElem = true;
|
|
}
|
|
}
|
|
|
|
return Optional.empty();
|
|
}
|
|
|
|
public Optional<IActivityElement> getNextElement(IActivityElement element) {
|
|
if (!hasElements())
|
|
return Optional.empty();
|
|
|
|
Iterator<Entry<String, IActivityElement>> iter = elementIterator();
|
|
IActivityElement previous = iter.next().getValue();
|
|
|
|
while (iter.hasNext()) {
|
|
IActivityElement elem = iter.next().getValue();
|
|
if (previous == element)
|
|
return Optional.ofNullable(elem);
|
|
else
|
|
previous = elem;
|
|
}
|
|
|
|
return Optional.empty();
|
|
}
|
|
|
|
public Optional<IActivityElement> getNextElementByType(IActivityElement element, String type) {
|
|
if (!hasElements())
|
|
return Optional.empty();
|
|
|
|
Iterator<Entry<String, IActivityElement>> iter = elementIterator();
|
|
|
|
boolean foundElem = false;
|
|
IActivityElement elem;
|
|
while (iter.hasNext()) {
|
|
elem = iter.next().getValue();
|
|
|
|
if (foundElem && elem.getType().equals(type))
|
|
return Optional.of(elem);
|
|
else if (elem == element) {
|
|
foundElem = true;
|
|
}
|
|
}
|
|
|
|
return Optional.empty();
|
|
}
|
|
|
|
public List<IActivityElement> getElementsByType(String type) {
|
|
List<IActivityElement> elements = new ArrayList<>();
|
|
Iterator<Entry<String, IActivityElement>> iter = elementIterator();
|
|
while (iter.hasNext()) {
|
|
IActivityElement element = iter.next().getValue();
|
|
if (element.getType().equals(type))
|
|
elements.add(element);
|
|
}
|
|
return elements;
|
|
}
|
|
|
|
/**
|
|
* @return get the {@code LinkedHashMap} of {@code IActivityElements}
|
|
*/
|
|
public Map<String, IActivityElement> getElements() {
|
|
if (this.elements == null)
|
|
return Collections.emptyMap();
|
|
return this.elements;
|
|
}
|
|
|
|
/**
|
|
* @return the iterator for entries, which include the id as key and the {@link IActivityElement} as value
|
|
*/
|
|
public Iterator<Entry<String, IActivityElement>> elementIterator() {
|
|
if (this.elements == null)
|
|
return Collections.emptyIterator();
|
|
return this.elements.entrySet().iterator();
|
|
}
|
|
|
|
/**
|
|
* @return the stream for entries, which include the id as key and the {@link IActivityElement} as value
|
|
*/
|
|
public Stream<Entry<String, IActivityElement>> elementStream() {
|
|
if (this.elements == null)
|
|
return Stream.empty();
|
|
return this.elements.entrySet().stream();
|
|
}
|
|
|
|
public Stream<IActivityElement> streamElements() {
|
|
if (this.elements == null)
|
|
return Stream.empty();
|
|
return this.elements.values().stream();
|
|
}
|
|
|
|
public Stream<IActivityElement> streamElementsByType(String type) {
|
|
return streamElements().filter(e -> e.getType().equals(type));
|
|
}
|
|
|
|
public Stream<IActivityElement> streamElements(Predicate<IActivityElement> predicate) {
|
|
return streamElements().filter(predicate);
|
|
}
|
|
|
|
public Stream<Activity> streamActivities() {
|
|
return streamElements() //
|
|
.filter(IActivityElement::isActivity) //
|
|
.map(IActivityElement::asActivity);
|
|
}
|
|
|
|
public Stream<Activity> streamActivities(Predicate<Activity> predicate) {
|
|
return streamActivities().filter(predicate);
|
|
}
|
|
|
|
public Stream<Activity> streamActivitiesByType(String type) {
|
|
return streamActivities(a -> a.getType().equals(type));
|
|
}
|
|
|
|
public Stream<Action> streamActions() {
|
|
return streamElements() //
|
|
.filter(IActivityElement::isAction) //
|
|
.map(IActivityElement::asAction);
|
|
}
|
|
|
|
public Stream<Action> streamActions(Predicate<Action> predicate) {
|
|
return streamActions().filter(predicate);
|
|
}
|
|
|
|
public Stream<Action> streamActionsByType(String type) {
|
|
return streamActions(a -> a.getType().equals(type));
|
|
}
|
|
|
|
public Stream<Action> streamActionsDeep() {
|
|
return streamElements().flatMap(e -> {
|
|
if (e.isAction())
|
|
return Stream.of(e);
|
|
return e.asActivity().streamActionsDeep();
|
|
}).map(IActivityElement::asAction);
|
|
}
|
|
|
|
public Stream<Action> streamActionsDeep(Predicate<Action> predicate) {
|
|
return streamActionsDeep().filter(predicate);
|
|
}
|
|
|
|
public <T extends IActivityElement> T findElement(Predicate<IActivityElement> predicate,
|
|
Supplier<String> msgSupplier) {
|
|
@SuppressWarnings("unchecked") T t = (T) streamElements()
|
|
.filter(predicate)
|
|
.collect(singletonCollector(msgSupplier));
|
|
return t;
|
|
}
|
|
|
|
public List<IActivityElement> findElementsByType(String type) {
|
|
return findElements(e -> e.getType().equals(type));
|
|
}
|
|
|
|
public List<IActivityElement> findElements(Predicate<IActivityElement> predicate) {
|
|
return streamElements().filter(predicate).collect(toList());
|
|
}
|
|
|
|
public List<Action> findActions(Predicate<Action> predicate) {
|
|
return streamActions(predicate).collect(toList());
|
|
}
|
|
|
|
public List<Action> findActionsDeep(Predicate<Action> predicate) {
|
|
return streamActionsDeep().filter(predicate).collect(toList());
|
|
}
|
|
|
|
/**
|
|
* Returns all the actions as a flat list
|
|
*
|
|
* @return the list of actions
|
|
*/
|
|
public List<Action> getActionsAsFlatList() {
|
|
return streamActionsDeep().toList();
|
|
}
|
|
|
|
/**
|
|
* Returns all the actions in the entire hierarchy with the given state
|
|
*
|
|
* @param state the state of the action to return
|
|
*
|
|
* @return the list of actions with the given state
|
|
*/
|
|
public List<Action> getActionsWithState(State state) {
|
|
return streamActionsDeep(action -> action.getState() == state).toList();
|
|
}
|
|
|
|
/**
|
|
* Returns all the actions in the entire hierarchy with the given type
|
|
*
|
|
* @param type the type of action to return
|
|
*
|
|
* @return the list of actions with the given type
|
|
*/
|
|
public List<Action> getActionsByType(String type) {
|
|
return streamActionsDeep(action -> action.getType().equals(type)).toList();
|
|
}
|
|
|
|
public <T extends IActivityElement> T getElementByLocator(Locator locator) {
|
|
DBC.PRE.assertEquals("Locator is not for this activity!", getLocator(), locator.trim(3));
|
|
DBC.PRE.assertTrue("Locator must have at least 5 parts", locator.getSize() >= 4);
|
|
|
|
IActivityElement element = this;
|
|
for (int i = 3; i < locator.getSize(); i++) {
|
|
String next = locator.get(i);
|
|
|
|
if (!(element instanceof Activity)) {
|
|
String msg = "Invalid locator {0} with part {1} as not an Activity but deeper element specified";
|
|
throw new StrolchModelException(MessageFormat.format(msg, locator, next));
|
|
}
|
|
|
|
element = ((Activity) element).getElement(next);
|
|
if (element == null)
|
|
throw new StrolchElementNotFoundException(locator + " does not exist!");
|
|
}
|
|
|
|
@SuppressWarnings("unchecked") T t = (T) element;
|
|
return t;
|
|
}
|
|
|
|
@Override
|
|
public Activity asActivity() {
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Long getStart() {
|
|
long start = Long.MAX_VALUE;
|
|
if (this.elements == null)
|
|
return start;
|
|
Iterator<Entry<String, IActivityElement>> elementIterator = elementIterator();
|
|
while (elementIterator.hasNext()) {
|
|
IActivityElement element = elementIterator.next().getValue();
|
|
start = Math.min(start, element.getStart());
|
|
}
|
|
return start;
|
|
}
|
|
|
|
@Override
|
|
public Long getEnd() {
|
|
long end = 0L;
|
|
if (this.elements == null)
|
|
return end;
|
|
Iterator<Entry<String, IActivityElement>> elementIterator = elementIterator();
|
|
while (elementIterator.hasNext()) {
|
|
IActivityElement element = elementIterator.next().getValue();
|
|
end = Math.max(end, element.getEnd());
|
|
}
|
|
return end;
|
|
}
|
|
|
|
@Override
|
|
public State getState() {
|
|
if (this.elements == null || this.elements.isEmpty())
|
|
return State.CREATED;
|
|
return State.getState(this);
|
|
}
|
|
|
|
@Override
|
|
public PolicyDefs getPolicyDefs() {
|
|
if (this.policyDefs == null)
|
|
throw new StrolchPolicyException(getLocator() + " has no Policies defined!");
|
|
return this.policyDefs;
|
|
}
|
|
|
|
@Override
|
|
public PolicyDef getPolicyDef(Class<?> clazz) {
|
|
return getPolicyDefs().getPolicyDef(clazz);
|
|
}
|
|
|
|
@Override
|
|
public PolicyDef getPolicyDef(String type) {
|
|
return getPolicyDefs().getPolicyDef(type);
|
|
}
|
|
|
|
@Override
|
|
public PolicyDef getPolicyDef(Class<?> clazz, PolicyDef defaultDef) {
|
|
if (!hasPolicyDefs())
|
|
return defaultDef;
|
|
return getPolicyDefs().getPolicyDef(clazz, defaultDef);
|
|
}
|
|
|
|
@Override
|
|
public PolicyDef getPolicyDef(String type, PolicyDef defaultDef) {
|
|
if (!hasPolicyDefs())
|
|
return defaultDef;
|
|
return getPolicyDefs().getPolicyDef(type, defaultDef);
|
|
}
|
|
|
|
@Override
|
|
public boolean hasPolicyDefs() {
|
|
return this.policyDefs != null;
|
|
}
|
|
|
|
@Override
|
|
public boolean hasPolicyDef(String type) {
|
|
return this.policyDefs != null && this.policyDefs.hasPolicyDef(type);
|
|
}
|
|
|
|
@Override
|
|
public boolean hasPolicyDef(Class<?> clazz) {
|
|
return this.policyDefs != null && this.policyDefs.hasPolicyDef(clazz);
|
|
}
|
|
|
|
@Override
|
|
public void setPolicyDefs(PolicyDefs policyDefs) {
|
|
assertNotReadonly();
|
|
this.policyDefs = policyDefs;
|
|
this.policyDefs.setParent(this);
|
|
}
|
|
|
|
@Override
|
|
public void addOrUpdate(PolicyDef policyDef) {
|
|
assertNotReadonly();
|
|
DBC.PRE.assertNotNull("policyDef", policyDef);
|
|
if (this.policyDefs == null) {
|
|
this.policyDefs = new PolicyDefs();
|
|
this.policyDefs.setParent(this);
|
|
}
|
|
this.policyDefs.addOrUpdate(policyDef);
|
|
}
|
|
|
|
@Override
|
|
public Locator getLocator() {
|
|
if (this.locator == null) {
|
|
LocatorBuilder lb = new LocatorBuilder();
|
|
fillLocator(lb);
|
|
this.locator = lb.build();
|
|
}
|
|
return this.locator;
|
|
}
|
|
|
|
@Override
|
|
protected void fillLocator(LocatorBuilder locatorBuilder) {
|
|
if (this.parent != null)
|
|
this.parent.fillLocator(locatorBuilder);
|
|
else
|
|
locatorBuilder.append(Tags.ACTIVITY).append(getType());
|
|
locatorBuilder.append(getId());
|
|
}
|
|
|
|
@Override
|
|
public Activity getParent() {
|
|
return this.parent;
|
|
}
|
|
|
|
@Override
|
|
public Activity getRootElement() {
|
|
return (this.parent == null) ? this : this.parent.getRootElement();
|
|
}
|
|
|
|
@Override
|
|
public boolean isRootElement() {
|
|
return (this.parent == null);
|
|
}
|
|
|
|
@Override
|
|
public Activity getClone() {
|
|
return getClone(false);
|
|
}
|
|
|
|
@Override
|
|
public Activity getClone(boolean withVersion) {
|
|
Activity clone = new Activity();
|
|
super.fillClone(clone);
|
|
clone.timeOrdering = this.timeOrdering;
|
|
clone.locator = this.locator;
|
|
|
|
if (this.elements != null) {
|
|
for (IActivityElement element : this.elements.values()) {
|
|
clone.addElement(element.getClone());
|
|
}
|
|
}
|
|
|
|
if (this.policyDefs != null)
|
|
clone.policyDefs = this.policyDefs.getClone();
|
|
if (withVersion)
|
|
clone.version = this.version;
|
|
return clone;
|
|
}
|
|
|
|
@Override
|
|
public void setReadOnly() {
|
|
if (this.policyDefs != null)
|
|
this.policyDefs.setReadOnly();
|
|
elementStream().forEach(e -> e.getValue().setReadOnly());
|
|
super.setReadOnly();
|
|
}
|
|
|
|
@Override
|
|
public Activity ensureModifiable() {
|
|
if (!this.isRootElement())
|
|
throw new IllegalStateException("Only call this method on the root element!");
|
|
if (isReadOnly())
|
|
return getClone(true);
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Activity ensureReadOnly() {
|
|
if (isReadOnly())
|
|
return this;
|
|
return getClone(true).readOnly();
|
|
}
|
|
|
|
@Override
|
|
public Activity readOnly() {
|
|
setReadOnly();
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (this == obj)
|
|
return true;
|
|
if (obj == null || getClass() != obj.getClass())
|
|
return false;
|
|
Activity a = (Activity) obj;
|
|
return this.parent == a.parent && this.type.equals(a.type) && this.id.equals(a.id);
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(type, id);
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
final StringBuilder builder = new StringBuilder();
|
|
builder.append(getLocator());
|
|
builder.append(", state=");
|
|
builder.append(getState());
|
|
builder.append(", start=");
|
|
builder.append(getStart());
|
|
builder.append(", end=");
|
|
builder.append(getEnd());
|
|
if (isRootElement()) {
|
|
builder.append(", version=");
|
|
builder.append(this.version.getVersion());
|
|
}
|
|
return builder.toString();
|
|
}
|
|
|
|
@Override
|
|
public int compareTo(Activity o) {
|
|
return getId().compareTo(o.getId());
|
|
}
|
|
|
|
@Override
|
|
public <T> T accept(StrolchElementVisitor<T> visitor) {
|
|
return visitor.visitActivity(this);
|
|
}
|
|
|
|
@Override
|
|
public <U, T extends Parameter<U>> T findObjectivesParam(String paramKey) {
|
|
return findParameter(BAG_OBJECTIVES, paramKey);
|
|
}
|
|
|
|
@Override
|
|
public <U, T extends Parameter<U>> T findObjectivesParam(String paramKey, boolean assertExists) {
|
|
return findParameter(BAG_OBJECTIVES, paramKey, assertExists);
|
|
}
|
|
|
|
@Override
|
|
public <U, T extends Parameter<U>> T findRelationParam(String paramKey) {
|
|
return findParameter(BAG_RELATIONS, paramKey);
|
|
}
|
|
|
|
@Override
|
|
public <U, T extends Parameter<U>> T findRelationParam(String paramKey, boolean assertExists) {
|
|
return findParameter(BAG_RELATIONS, paramKey, assertExists);
|
|
}
|
|
|
|
@Override
|
|
public <U, T extends Parameter<U>> T findParameter(String paramKey) {
|
|
return findParameter(BAG_PARAMETERS, paramKey);
|
|
}
|
|
|
|
@Override
|
|
public <U, T extends Parameter<U>> T findParameter(String paramKey, boolean assertExists) {
|
|
return findParameter(BAG_PARAMETERS, paramKey, assertExists);
|
|
}
|
|
|
|
public <U, T extends Parameter<U>> T findParameter(String bagKey, String paramKey) {
|
|
|
|
T parameter = getParameter(bagKey, paramKey);
|
|
if (parameter != null)
|
|
return parameter;
|
|
|
|
if (this.parent != null)
|
|
return this.parent.findParameter(bagKey, paramKey);
|
|
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public <U, T extends Parameter<U>> T findParameter(String bagKey, String paramKey, boolean assertExists)
|
|
throws StrolchModelException {
|
|
|
|
T parameter = getParameter(bagKey, paramKey);
|
|
if (parameter != null)
|
|
return parameter;
|
|
|
|
parameter = this.parent == null ? null : this.parent.findParameter(bagKey, paramKey);
|
|
if (assertExists && parameter == null) {
|
|
String msg = "The Parameter {0} does not exist";
|
|
throw new StrolchModelException(MessageFormat.format(msg, getLocator().append(Tags.BAG, bagKey, paramKey)));
|
|
}
|
|
|
|
return parameter;
|
|
}
|
|
|
|
@Override
|
|
public PolicyDef findPolicy(Class<?> clazz, PolicyDef defaultDef) throws StrolchModelException {
|
|
return findPolicy(clazz.getSimpleName(), defaultDef);
|
|
}
|
|
|
|
@Override
|
|
public PolicyDef findPolicy(String className, PolicyDef defaultDef) throws StrolchModelException {
|
|
if (hasPolicyDef(className))
|
|
return getPolicyDef(className);
|
|
|
|
if (this.parent == null) {
|
|
if (defaultDef != null)
|
|
return defaultDef;
|
|
|
|
String msg = "The PolicyDef {0} does not exist";
|
|
throw new StrolchModelException(MessageFormat.format(msg, className));
|
|
}
|
|
|
|
return this.parent.findPolicy(className, defaultDef);
|
|
}
|
|
|
|
@Override
|
|
public void setParent(Activity activity) {
|
|
assertNotReadonly();
|
|
this.parent = activity;
|
|
this.locator = null;
|
|
}
|
|
|
|
/**
|
|
* Creates a {@link Locator} for activities of the given type and id
|
|
*
|
|
* @param type the type of activity
|
|
* @param id the id of the activity
|
|
*
|
|
* @return the locator
|
|
*/
|
|
public static Locator locatorFor(String type, String id) {
|
|
return Locator.valueOf(Tags.ACTIVITY, type, id);
|
|
}
|
|
|
|
/**
|
|
* Parses the given XML and returns the activity with the given ID
|
|
*
|
|
* @param xml the xml to parse
|
|
* @param id the id of the activity to return from the parsed elements
|
|
*
|
|
* @return the activity, or null if it does not exist
|
|
*/
|
|
public static Resource parse(String xml, String id) {
|
|
return StrolchXmlHelper.parseAndReturnResource(xml, id);
|
|
}
|
|
}
|