/*
* 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 java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import li.strolch.exception.StrolchException;
import li.strolch.exception.StrolchPolicyException;
import li.strolch.model.GroupedParameterizedElement;
import li.strolch.model.Locator;
import li.strolch.model.Locator.LocatorBuilder;
import li.strolch.model.PolicyContainer;
import li.strolch.model.State;
import li.strolch.model.StrolchElement;
import li.strolch.model.StrolchRootElement;
import li.strolch.model.Tags;
import li.strolch.model.policy.PolicyDefs;
import li.strolch.model.visitor.StrolchRootElementVisitor;
import li.strolch.utils.dbc.DBC;
/**
* 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 GroupedParameterizedElement
implements IActivityElement, StrolchRootElement, Comparable<Activity>, PolicyContainer {
private static final long serialVersionUID = 1L;
protected Activity parent;
protected Map<String, IActivityElement> elements;
protected PolicyDefs policyDefs;
/**
* Empty constructor - for marshalling only!
*/
public Activity() {
super();
}
public Activity(String id, String name, String type) {
super(id, name, type);
}
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<>();
}
}
/**
* 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</code> of <code>IActivityElements</code>
*
* @param activityElement
* @return the element added
*/
public IActivityElement addElement(IActivityElement activityElement) {
DBC.PRE.assertNotEquals("Can't add element to itself!", this, activityElement);
DBC.PRE.assertNull("Parent can't already be set!", activityElement.getParent());
// TODO make sure we can't create a circular dependency
initElements();
String id = activityElement.getId();
if (id == null)
throw new StrolchException("Cannot add IActivityElement without id.");
else if (elements.containsKey(id))
throw new StrolchException(
"Activiy " + getLocator() + " already contains an activity element with id = " + id);
else {
activityElement.setParent(this);
return elements.put(activityElement.getId(), activityElement);
}
}
/**
* get <code>IActivityElement</code> by id
*
* @param id
* the id of the <code>IActivityElement</code>
* @return IActivityElement
*/
@SuppressWarnings("unchecked")
public <T extends IActivityElement> T getElement(String id) {
if (this.elements == null)
return null;
return (T) elements.get(id);
}
/**
* @return get the <code>LinkedHashMap</code> of <code>IActivityElements</code>
*/
public Map<String, IActivityElement> getElements() {
if (this.elements == null)
return Collections.emptyMap();
return 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.<String, IActivityElement> emptyMap().entrySet().iterator();
return elements.entrySet().iterator();
}
@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 action = elementIterator.next().getValue();
start = Math.min(start, action.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 action = elementIterator.next().getValue();
end = Math.max(end, action.getEnd());
}
return end;
}
@Override
public State getState() {
State state = State.PLANNED;
if (this.elements == null)
return state;
Iterator<Entry<String, IActivityElement>> elementIterator = elementIterator();
while (elementIterator.hasNext()) {
IActivityElement child = elementIterator.next().getValue();
State childState = child.getState();
if (childState.ordinal() < state.ordinal()) {
state = childState;
}
}
return state;
}
@Override
public PolicyDefs getPolicyDefs() {
if (this.policyDefs == null)
throw new StrolchPolicyException(getLocator() + " has no Policies defined!");
return this.policyDefs;
}
@Override
public boolean hasPolicyDefs() {
return this.policyDefs != null;
}
@Override
public void setPolicyDefs(PolicyDefs policyDefs) {
this.policyDefs = policyDefs;
this.policyDefs.setParent(this);
}
@Override
public Locator getLocator() {
LocatorBuilder lb = new LocatorBuilder();
fillLocator(lb);
return lb.build();
}
@Override
protected void fillLocator(LocatorBuilder locatorBuilder) {
locatorBuilder.append(Tags.ACTIVITY).append(getType()).append(getId());
}
@Override
public StrolchElement getParent() {
return parent;
}
@Override
public StrolchRootElement getRootElement() {
return (parent == null) ? this : parent.getRootElement();
}
@Override
public boolean isRootElement() {
return (parent == null);
}
@Override
public Activity getClone() {
Activity clone = new Activity();
super.fillClone(clone);
if (this.elements == null)
return clone;
for (IActivityElement element : this.elements.values()) {
clone.addElement(element.getClone());
}
if (this.policyDefs != null)
clone.setPolicyDefs(this.policyDefs.getClone());
return clone;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("Activity [id=");
builder.append(this.id);
builder.append(", name=");
builder.append(this.name);
builder.append(", type=");
builder.append(this.type);
builder.append(", state=");
builder.append(this.getState());
builder.append(", start=");
builder.append(this.getStart());
builder.append(", end=");
builder.append(this.getEnd());
builder.append("]");
return builder.toString();
}
@Override
public int compareTo(Activity o) {
return getId().compareTo(o.getId());
}
@Override
public <T> T accept(StrolchRootElementVisitor<T> visitor) {
throw new StrolchException("not implemented yet");
}
@Override
public void setParent(Activity activity) {
this.parent = activity;
}
}