/*
* Copyright 2002-2005 the original author or authors.
*
* 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 info.jtrac.domain;
import info.jtrac.util.XmlUtils;
import java.util.HashSet;
import static info.jtrac.Constants.*;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.dom4j.Element;
/**
* <p>
* State as in "State Transition" holds a set of possible future states to
* transition. It also holds a map of <code>[field name = integer "mask"]</code>
* to represent permissions (view or edit) that the {@link Role} owning this
* state has for each field for an item which is in this particular state.
* </p>
* <p>
* For example, consider a state FOO and a role BAR.<br />
* When a {@link User} with {@link Role} BAR views an item that is having the
* status FOO:
* ie. when <code>item.status == FOO.status</code>, the fields that can be viewed on screen
* will be the entries in FOO.fields where the value == {@link #MASK_READONLY} (1)
* </p>
*/
public class State implements Serializable {
/**
* Generated UID
*/
private static final long serialVersionUID = -6321200313541210811L;
/**
* The status of the state.
*/
private int status;
/**
* The {@link Set} of transitions to other {@link State} objects.
*/
private Set<Integer> transitions = new HashSet<Integer>();
/**
* The {@link Map} of {@link Field} objects.
*/
private Map<Field.Name, Integer> fields = new HashMap<Field.Name, Integer>();
/**
* The predefined NEW state.
*/
public static final int NEW = 0;
/**
* The predefined OPEN state.
*/
public static final int OPEN = 1;
/**
* The predefined CLOSED state.
*/
public static final int CLOSED = 99;
/**
* The predefined HIDDEN mask state.
*/
public static final int MASK_HIDDEN = 0;
public static final int MASK_READONLY = 1;
public static final int MASK_OPTIONAL = 2;
public static final int MASK_MANDATORY = 3;
/**
* Default empty constructor.
*/
public State() {
}
/**
* This constructor will set the {@link #status} of this state.
*
* @param status The status of the state.
*/
public State(int status) {
this.status = status;
}
/**
* This constructor will read the status of the state from the given
* {@link Element} attribute. Then all Transitions elements are read
* to add them to the map of {@link #transitions}. Finally all Field
* elements are read to add them to the map of {@link #fields}.
*
* @param element The {@link Element} to read and process.
*/
public State(Element element) {
this.status = Integer.parseInt(element.attributeValue(STATUS));
for (Object o : element.elements(TRANSITION)) {
Element t = (Element) o;
transitions.add(new Integer(t.attributeValue(STATUS)));
} // end for each
for (Object o : element.elements(FIELD)) {
Element f = (Element) o;
String fieldName = f.attributeValue(NAME);
fields.put(Field.convertToName(fieldName), new Integer(f.attributeValue(MASK)));
} // end for each
}
/**
* This method will append this object to an existing XML document.
*
* @param parent The parent to apply this state to.
*/
public void addAsChildOf(Element parent) {
Element e = parent.addElement(STATE);
copyTo(e);
}
/**
* This method will marshal this object into a fresh new XML element.
*
* @return Returns the state as XML {@link Element}.
*/
public Element getAsElement() {
Element e = XmlUtils.getNewElement(STATE);
copyTo(e);
return e;
}
/**
* Copy object values into an existing XML element.
*
* @param element The {@link Element} object to append.
*/
private void copyTo(Element element) {
// Appending empty strings to create new objects for "clone" support
element.addAttribute(STATUS, status + "");
for (Integer toStatus : transitions) {
Element t = element.addElement(TRANSITION);
t.addAttribute(STATUS, toStatus + "");
} // end for each
for (Map.Entry<Field.Name, Integer> entry : fields.entrySet()) {
Element f = element.addElement(FIELD);
f.addAttribute(NAME, entry.getKey() + "");
f.addAttribute(MASK, entry.getValue() + "");
} // end for each
}
/**
* This method allows to add a {@link State} to the map of {@link #fields}.
*
* @param fieldNames The {@link Collection} of {@link Field.Name} objects
* to add to the map.
*/
public void add(Collection<Field.Name> fieldNames) {
for (Field.Name fieldName : fieldNames) {
add(fieldName);
} // end for each
}
/**
* This method allows to add a {@link State} to the map of {@link #fields}.
*
* @param fieldName The {@link Field.Name} to add to the map.
*/
public void add(Field.Name fieldName) {
int mask = MASK_READONLY;
// For NEW states, normally all Fields on the Item are editable
if (status == NEW) {
mask = MASK_MANDATORY;
}
fields.put(fieldName, mask);
}
/**
* This method allows to remove the specified field name from the map of
* {@link #fields}.
*
* @param fieldName The field name to remove from the map of {@link #fields}.
*/
public void remove(Field.Name fieldName) {
fields.remove(fieldName);
}
/**
* This method allows to add a transition to the set of {@link #transitions}.
*
* @param toStatus The transition to add to the set.
*/
public void addTransition(int toStatus) {
transitions.add(toStatus);
}
/**
* This method allows to remove the specified transition from the set of
* {@link #transitions}.
*
* @param toStatus The transition to remove from the set of {@link #transitions}.
*/
public void removeTransition(int toStatus) {
transitions.remove(toStatus);
}
/**
* This method returns the map of {@link #fields}.
*
* @return Returns {@link #fields}.
*/
public Map<Field.Name, Integer> getFields() {
return fields;
}
/**
* This method allow to store a map of {@link #fields}.
*
* @param fields The map of {@link #fields} to store.
*/
public void setFields(Map<Field.Name, Integer> fields) {
this.fields = fields;
}
/**
* This method returns the {@link #status} of the state.
*
* @return Returns {@link #status}.
*/
public int getStatus() {
return status;
}
/**
* This method allows to store the {@link #status} of the state.
*
* @param status The {@link #status} of the state.
*/
public void setStatus(int status) {
this.status = status;
}
/**
* This method returns the set of {@link #transitions}.
*
* @return Returns {@link #transitions}.
*/
public Set<Integer> getTransitions() {
return transitions;
}
/**
* This method allow to store a set of {@link #transitions}.
*
* @param fields The set of {@link #transitions} to store.
*/
public void setTransitions(Set<Integer> transitions) {
this.transitions = transitions;
}
/**
* This method overrides the default {@link Object#toString()} method to
* return the string representation of this object.
*
* @return Returns a string representation of the object.
*/
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("status [").append(status);
sb.append("]; transitions [").append(transitions);
sb.append("]; fields [").append(fields);
sb.append("]");
return sb.toString();
}
}