001: /*
002: * Created on Aug 4, 2005
003: */
004: package uk.org.ponder.messageutil;
005:
006: import java.io.Serializable;
007: import java.util.ArrayList;
008:
009: import uk.org.ponder.stringutil.CharWrap;
010: import uk.org.ponder.stringutil.StringList;
011:
012: /**
013: * A typesafe container of {@link TargettedMessage} objects. It maintains the
014: * hidden state of a field <code>nestedpath</code> which corresponds to the EL
015: * location for which the data alterations currently being processed are based
016: * at. TargettedMessage objects received to this list via the
017: * {@link #addMessage(TargettedMessage)} methods will have their
018: * <code>targetid</code> field, if set, interpreted as an EL relative to this
019: * hidden path and rebased.
020: *
021: * @author Antranig Basman (antranig@caret.cam.ac.uk)
022: */
023:
024: public class TargettedMessageList implements Serializable {
025: /** A nestedpath ending with this String will consume the following path
026: * segments
027: */
028: public static final String BACKUP_PATH = "..";
029: // NB - this class is ALMOST identical to the Spring "Errors" interface,
030: // which I was put off since the only implementation there is "BindException"
031: // which couples to all sorts of other greasy stuff, including the dreaded
032: // BeanWrapper. However, it may be worth going back to Errors and Validator
033: // at least, which are fairly clean in of themselves.
034: private ArrayList errors = new ArrayList();
035:
036: /** Returns the number of messages held in this list * */
037: public int size() {
038: return errors.size();
039: }
040:
041: /**
042: * Appends a new TargettedMessage to this list. The <code>targetid</code>
043: * field will be interpreted as an EL path adjusted to take into account of
044: * the current value of <code>nestedPath</code>
045: */
046: public void addMessage(TargettedMessage message) {
047: if (nestedpath.length() != 0
048: && !message.targetid
049: .equals(TargettedMessage.TARGET_NONE)) {
050: if (nestedpath.endsWith(BACKUP_PATH)) {
051: message.targetid = nestedpath.substring(0, nestedpath
052: .length()
053: - BACKUP_PATH.length());
054: } else {
055: message.targetid = nestedpath + message.targetid;
056: }
057: }
058: errors.add(message);
059: }
060:
061: /**
062: * Appends multiple messages to this list, through multiple calls to
063: * {@link #addMessage(TargettedMessage)}.
064: */
065:
066: public void addMessages(TargettedMessageList list) {
067: for (int i = 0; i < list.size(); ++i) {
068: addMessage(list.messageAt(i));
069: }
070: }
071:
072: /**
073: * Does the current state of the TML represent an error? This is determined by
074: * checking for any individual message which is at the
075: * {@link TargettedMessage#SEVERITY_ERROR} severity level.
076: */
077: public boolean isError() {
078: for (int i = 0; i < size(); ++i) {
079: TargettedMessage mess = messageAt(i);
080: if ((mess.severity & 1) == TargettedMessage.SEVERITY_ERROR)
081: return true;
082: }
083: return false;
084: }
085:
086: private StringList pathstack;
087:
088: private String nestedpath = "";
089:
090: /** Push the supplied path segment onto the hidden path base * */
091: public void pushNestedPath(String extrapath) {
092: if (extrapath == null) {
093: extrapath = "";
094: }
095: if (extrapath.length() > 0 && !extrapath.endsWith(".")) {
096: extrapath += '.';
097: }
098: if (pathstack == null) {
099: pathstack = new StringList();
100: }
101: pathstack.add(nestedpath);
102: this .nestedpath = nestedpath + extrapath;
103: }
104:
105: /** Pops one path segment off the hidden path base * */
106:
107: public void popNestedPath() {
108: int top = pathstack.size() - 1;
109: nestedpath = pathstack.stringAt(top);
110: pathstack.remove(top);
111: }
112:
113: /**
114: * Access a message by index.
115: *
116: * @param i The index of the message to access.
117: * @return The TargettedMessage at index position <code>i</code>
118: */
119: public TargettedMessage messageAt(int i) {
120: return (TargettedMessage) errors.get(i);
121: }
122:
123: /**
124: * Update a message held at a particular index.
125: *
126: * @param i The index of the message to update
127: * @param message The new message to be held at index <code>i</code>
128: */
129: public void setMessageAt(int i, TargettedMessage message) {
130: errors.set(i, message);
131: }
132:
133: /** Clears all messages from this list * */
134:
135: public void clear() {
136: errors.clear();
137: }
138:
139: /**
140: * Pack this list into a String for debugging purposes.
141: */
142: public String pack() {
143: CharWrap togo = new CharWrap();
144: for (int i = 0; i < size(); ++i) {
145: TargettedMessage mess = messageAt(i);
146: togo.append("Target: " + mess.targetid + " message "
147: + mess.acquireMessageCode() + "\n");
148: }
149: return togo.toString();
150: }
151:
152: /**
153: * Renders the entire contents of this message list into a list of localised
154: * messages, using the supplied <code>MessageLocator</code>
155: */
156: public StringList render(MessageLocator locator) {
157: StringList togo = new StringList();
158: for (int i = 0; i < size(); ++i) {
159: TargettedMessage message = messageAt(i);
160: togo.add(locator.getMessage(message.messagecodes,
161: message.args));
162: }
163: return togo;
164: }
165:
166: }
|