001: /*
002: * Created on Nov 14, 2005
003: */
004: package uk.org.ponder.rsf.renderer;
005:
006: import java.util.Map;
007:
008: import uk.org.ponder.messageutil.TargettedMessage;
009: import uk.org.ponder.messageutil.TargettedMessageList;
010: import uk.org.ponder.rsf.components.ComponentList;
011: import uk.org.ponder.rsf.components.UIComponent;
012: import uk.org.ponder.rsf.components.UIContainer;
013: import uk.org.ponder.rsf.template.XMLLump;
014: import uk.org.ponder.rsf.template.XMLLumpList;
015: import uk.org.ponder.rsf.util.RSFUtil;
016: import uk.org.ponder.rsf.util.SplitID;
017: import uk.org.ponder.rsf.view.View;
018: import uk.org.ponder.stringutil.CharWrap;
019: import uk.org.ponder.util.Logger;
020:
021: public class MessageTargetter {
022: // a dummy key to be used to signify a target for messages whose targets
023: // cannot be found.
024: public static final XMLLump DEAD_LETTERS = new XMLLump();
025: public static final String RSF_MESSAGES = "rsf-messages:";
026:
027: public static class BestTarget {
028: public XMLLump container;
029: public CharWrap rootpath = new CharWrap();
030: public XMLLump bestfor = null;
031:
032: public void setContainer(XMLLump container) {
033: this .container = container;
034: }
035:
036: public void checkTarget(String ID) {
037: XMLLumpList forlumps = container.downmap.headsForID(ID);
038: if (forlumps != null && forlumps.size() > 0) {
039: bestfor = forlumps.lumpAt(0);
040: }
041: }
042: }
043:
044: /**
045: * For each message, find the MOST SPECIFIC message-for component in the tree
046: * claiming to accept it. Search starts at the very root of the tree, with the
047: * most generic target of all with simply the ID "message-for:*".
048: *
049: * @param branchmap
050: * A map of UIBranchContainers to XMLLumps, as prepared by pass 1 of
051: * the renderer.
052: * @param view
053: * The view being rendered - used for its component ID map.
054: * @param messages
055: * The list of messages to be distributed to message target
056: * components.
057: * @param globalmessagetarget
058: * The fullID of the "submitting control" for the previous view. This
059: * will form the SECOND LEVEL fallback after all targets in the
060: * GLOBAL ROOT have been searched. This should almost always be set.
061: */
062:
063: public static MessageTargetMap targetMessages(Map branchmap,
064: View view, TargettedMessageList messages,
065: String globalmessagetarget) {
066: MessageTargetMap togo = new MessageTargetMap();
067: if (messages == null)
068: return togo;
069:
070: BestTarget best = new BestTarget();
071: UIComponent globaltarget = globalmessagetarget == null ? null
072: : view.getComponent(globalmessagetarget);
073: ComponentList globalrootpath = globaltarget == null ? null
074: : RSFUtil.getRootPath(globaltarget);
075:
076: for (int i = 0; i < messages.size(); ++i) {
077: TargettedMessage message = messages.messageAt(i);
078: String targetid = message.targetid;
079: if (targetid.equals(TargettedMessage.TARGET_NONE)) {
080: targetid = globalmessagetarget;
081: }
082: best.bestfor = DEAD_LETTERS;
083: UIComponent target = view.getComponent(targetid);
084: if (targetid != null) {
085: int colpos = targetid.lastIndexOf(':');
086: if (colpos != -1) {
087: targetid = targetid.substring(colpos + 1);
088: }
089: }
090: // TODO: what if action has, despite errors, insisted on redirecting to
091: // a DIFFERENT view?
092: if (target == null) {
093: // remove this spurious warning for now. "globaltarget" system was
094: // intended to at least identify the right form in the view by means
095: // of the submitting control, but needs review. In the meantime, there
096: // is now the "DEAD_LETTER" last-ditch system for final delivery.
097: // Logger.log.warn("Warning: Message " + message.acquireMessageCode()
098: // + " queued for nonexistent component ID " + message.targetid);
099: } else {
100: SplitID split = new SplitID(targetid);
101: boolean hassuffix = split.suffix != null;
102: ComponentList rootpath = RSFUtil.getRootPath(target);
103: for (int j = 0; j < rootpath.size() - 1; ++j) {
104: if (globalrootpath != null
105: && j == globalrootpath.size() - 2) {
106: // if we are at the branch level of the "submitting control" (which
107: // as we recall will ***NOT*** be nested in the branch stack at
108: // this point, implement the first-level fallback check for
109: // messages targetted at it globally.
110: UIContainer globalbranch = (UIContainer) globalrootpath
111: .get(j);
112: XMLLump peer = (XMLLump) branchmap
113: .get(globalbranch);
114: best.setContainer(peer);
115: String search0 = XMLLump.FORID_PREFIX
116: + targetid;
117: best.checkTarget(search0);
118: }
119: UIContainer branch = (UIContainer) rootpath.get(j);
120: XMLLump peer = (XMLLump) branchmap.get(branch);
121: best.setContainer(peer);
122: best.checkTarget(XMLLump.FORID_PREFIX);
123: String search1 = XMLLump.FORID_PREFIX
124: + split.prefix;
125: best.checkTarget(search1);
126: if (hassuffix) {
127: String search2 = XMLLump.FORID_PREFIX
128: + targetid;
129: best.checkTarget(search2);
130: }
131: }
132: }
133: if (best.bestfor != null) {
134: togo.setTarget(best.bestfor, message);
135: } else {
136: // well noone can say we didn't try our darndest to deliver this message.
137: Logger.log.error("Unable to deliver message "
138: + message.acquireMessageCode()
139: + " targetted at " + message.targetid);
140: }
141: }
142: return togo;
143: }
144: }
|