001: package org.ztemplates.actions.urlhandler.tree.match;
002:
003: import java.io.Serializable;
004: import java.util.ArrayList;
005: import java.util.Collections;
006: import java.util.List;
007:
008: import org.apache.log4j.Logger;
009: import org.ztemplates.actions.ZMatch;
010: import org.ztemplates.actions.urlhandler.ZUrlCollisionException;
011: import org.ztemplates.actions.urlhandler.tree.term.ZTreeLiteral;
012: import org.ztemplates.actions.urlhandler.tree.term.ZTreeSlash;
013: import org.ztemplates.actions.urlhandler.tree.term.ZTreeTail;
014: import org.ztemplates.actions.urlhandler.tree.term.ZTreeTerm;
015: import org.ztemplates.actions.urlhandler.tree.term.ZTreeTermFactory;
016: import org.ztemplates.actions.urlhandler.tree.term.ZTreeTermList;
017: import org.ztemplates.actions.urlhandler.tree.term.ZTreeVariable;
018: import org.ztemplates.actions.util.ZFormatUtil;
019: import org.ztemplates.classpath.ZIClassRepository;
020:
021: public class ZMatchTree implements Serializable {
022: static Logger log = Logger.getLogger(ZMatchTree.class);
023:
024: private final List<ZMatchTreeNode> roots = new ArrayList<ZMatchTreeNode>();
025:
026: public ZMatchTree() {
027: }
028:
029: public ZMatchTree(ZIClassRepository classRepository)
030: throws Exception {
031: for (Class c : classRepository
032: .getClassesAnnotatedWith(ZMatch.class)) {
033: ZMatch m = (ZMatch) c.getAnnotation(ZMatch.class);
034: if (m.value().charAt(0) == '/') {
035: List<ZTreeTermList> terms = ZTreeTermFactory.expand(
036: classRepository, c);
037: for (ZTreeTermList crt : terms) {
038: add(crt);
039: }
040: }
041: }
042: prepare();
043: }
044:
045: public void prepare() {
046: Collections.sort(roots);
047: for (ZMatchTreeNode node : roots) {
048: node.sortChildren();
049: }
050: }
051:
052: public void add(ZTreeTermList crt) throws Exception {
053: List<ZTreeTerm> terms = crt.getTerms();
054: List<ZMatchTreeNode> nodes = createNodes(terms);
055: merge(roots, nodes, 0);
056: }
057:
058: /**
059: * create new nodes when found slash, terms must start with slash
060: *
061: * @param terms
062: * @return
063: */
064: private List<ZMatchTreeNode> createNodes(List<ZTreeTerm> terms) {
065: List<ZMatchTreeNode> ret = new ArrayList<ZMatchTreeNode>();
066: ZMatchTreeNode crt = null;
067: for (ZTreeTerm term : terms) {
068: if (term instanceof ZTreeSlash) {
069: ZMatchTreeNode newNode = new ZMatchTreeNode();
070: ret.add(newNode);
071: crt = newNode;
072: } else if (term instanceof ZTreeLiteral) {
073: ZTreeLiteral tl = (ZTreeLiteral) term;
074: crt.getSegments()
075: .add(new ZSegmentLiteral(tl.getText()));
076: } else if (term instanceof ZTreeVariable) {
077: ZTreeVariable tl = (ZTreeVariable) term;
078: crt.getSegments().add(
079: new ZSegmentVariable(tl.getName()));
080: } else if (term instanceof ZTreeTail) {
081: ZTreeTail tl = (ZTreeTail) term;
082: crt.getSegments().add(new ZSegmentTail(tl.getName()));
083: }
084: }
085: crt.setHandler(new ZTreeTermList(terms));
086: return ret;
087: }
088:
089: /**
090: * merge nodes into r
091: *
092: * @param root2
093: * @param nodes
094: */
095: private void merge(List<ZMatchTreeNode> parentNodes,
096: List<ZMatchTreeNode> nodes, int start) throws Exception {
097: if (start >= nodes.size()) {
098: return;
099: }
100: ZMatchTreeNode crtNode = nodes.get(start);
101: boolean added = false;
102: for (ZMatchTreeNode parentNode : parentNodes) {
103: if (crtNode.isMatchingTheSame(parentNode)) {
104: if (crtNode.getHandler() != null) {
105: if (parentNode.getHandler() == null) {
106: parentNode.setHandler(crtNode.getHandler());
107: } else {
108: throw new ZUrlCollisionException(
109: "url collision: \n"
110: + parentNode.getHandler()
111: .printToConsole()
112: + "\n"
113: + crtNode.getHandler()
114: .printToConsole());
115: }
116: }
117: crtNode = parentNode;
118: added = true;
119: break;
120: }
121: }
122: if (!added) {
123: parentNodes.add(crtNode);
124: }
125: merge(crtNode.getChildren(), nodes, start + 1);
126: }
127:
128: @Override
129: public String toString() {
130: return toConsoleString();
131: // StringBuffer sb = new StringBuffer();
132: // toXml(sb, 0);
133: // return sb.toString();
134: }
135:
136: public void toXml(StringBuffer sb, int depth) {
137: ZFormatUtil.indentTree(sb, depth);
138: sb.append("<parse-tree>");
139: for (ZMatchTreeNode node : roots) {
140: node.toXml(sb, depth + 1);
141: }
142: ZFormatUtil.indentTree(sb, depth);
143: sb.append("</parse-tree>");
144: }
145:
146: public String toConsoleString() {
147: // first, compute max width of tree
148: int fill = Integer.MIN_VALUE;
149: StringBuffer sb = new StringBuffer();
150: for (ZMatchTreeNode node : roots) {
151: fill = Math.max(fill, printToConsole(sb, node, "", 0));
152: }
153: fill += 4;
154: // then print tree
155: sb = new StringBuffer();
156: for (ZMatchTreeNode node : roots) {
157: printToConsole(sb, node, "", fill);
158: }
159: return sb.toString();
160: }
161:
162: private int printToConsole(StringBuffer ret, ZMatchTreeNode node,
163: String prefix, int fill) {
164: StringBuffer sb = new StringBuffer();
165: sb.append('\n');
166: if (prefix.length() > 0) {
167: if (prefix.charAt(prefix.length() - 1) == '|') {
168: sb.append(prefix);
169: sb.append("-- /");
170: } else {
171: sb.append(prefix.substring(0, prefix.length() - 1));
172: sb.append("|-- /");
173: }
174: } else {
175: sb.append("/");
176: }
177: for (ZSegment t : node.getSegments()) {
178: String s = t.toDefinition();
179: sb.append(s);
180: }
181:
182: int len = sb.length();
183:
184: if (node.getHandler() != null) {
185: sb.append(" ");
186: ZFormatUtil.fillRight(sb, fill, '.');
187: String s = node.getHandler().printToConsole();
188: sb.append(s);
189: }
190:
191: ret.append(sb);
192:
193: for (int i = 0; i < node.getChildren().size(); i++) {
194: ZMatchTreeNode child = node.getChildren().get(i);
195: String crtPrefix;
196: if (i == node.getChildren().size() - 1) {
197: crtPrefix = prefix + " ";
198: } else {
199: crtPrefix = prefix + " |";
200: }
201: len = Math.max(len, printToConsole(ret, child, crtPrefix,
202: fill));
203: }
204:
205: return len;
206: }
207:
208: public List<ZMatchTreeNode> getRoots() {
209: return roots;
210: }
211:
212: public ZMatchedUrl match(String url) {
213: for (ZMatchTreeNode node : roots) {
214: List<String> values = new ArrayList<String>();
215: ZTreeTermList terms = node.match(url, values);
216: if (terms != null) {
217: return new ZMatchedUrl(terms, values);
218: }
219: }
220: return null;
221: }
222: }
|