KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > util > functions > IndexFunction


1 package org.mmbase.util.functions;
2
3 import java.util.*;
4 import java.util.regex.*;
5
6 import org.mmbase.cache.Cache;
7 import org.mmbase.bridge.*;
8 import org.mmbase.bridge.util.*;
9 import org.mmbase.util.transformers.RomanTransformer;
10 import org.mmbase.module.core.*;
11 import org.mmbase.storage.search.*;
12 import org.mmbase.util.logging.Logger;
13 import org.mmbase.util.logging.Logging;
14
15 /**
16  * The index node functions can be assigned to nodes which are connected by an 'index' relation. An
17  * index relation is an extension of 'posrel', but also has an 'index' and a 'root' field. These are
18  * used to calcaluate the 'number' of the connected nodes. The 'pos' field only serves to fix the order.
19  *
20  * The index field can be empty in which case the node with the lowest pos gets number 1, the
21  * following number 2 and so on. But on any one of the index relations the index can be stated
22  * explicitely. If e.g. the index is specified 'a' then the following node will be 'b'. You can also
23  * arrange for 'i', 'ii', 'iii', 'iv' and so on.
24  *
25  * The root field (a node-type field) specifies to which tree the relations belong. So principaly
26  * the same 'chapter' can exists with several different chapter numbers. Also, it is used to define
27  * where counting must starts, because the complete number of a chapter consists of a chain of
28  * numbers (like 2.3.4.iii).
29  *
30  *
31  * @author Michiel Meeuwissen
32  * @version $Id: IndexFunction.java,v 1.9 2006/08/30 17:48:52 michiel Exp $
33  * @since MMBase-1.8
34  */

35 public class IndexFunction extends FunctionProvider {
36
37     private static final Logger log = Logging.getLoggerInstance(IndexFunction.class);
38
39     protected static Cache indexCache = new Cache(400) {
40             public String JavaDoc getName() {
41                 return "IndexNumberCache";
42             }
43             public String JavaDoc getDescription() {
44                 return "rootNumber/objectNumber -> Index";
45             }
46
47         };
48
49     static {
50         indexCache.putCache();
51
52     }
53
54     private static MMBaseObserver observer = null;
55     private static synchronized void initObserver() {
56         if (observer == null) {
57             MMBaseObserver o = null;
58             try {
59                  o = new MMBaseObserver() {
60                         public boolean nodeRemoteChanged(String JavaDoc machine, String JavaDoc number, String JavaDoc builder, String JavaDoc ctype) {
61                             return nodeChanged(machine, number, builder, ctype);
62                         }
63                         public boolean nodeLocalChanged(String JavaDoc machine, String JavaDoc number, String JavaDoc builder, String JavaDoc ctype) {
64                             return nodeChanged(machine, number, builder, ctype);
65                         }
66                         public boolean nodeChanged(String JavaDoc machine, String JavaDoc number, String JavaDoc builder, String JavaDoc ctype) {
67                             log.info("Received change " + machine + "/" + number + "/" + builder + "/" + ctype);
68                             indexCache.clear(); // this could be done smarter.
69
return true;
70                         }
71
72
73                     };
74                 MMObjectBuilder indexRelation = MMBase.getMMBase().getBuilder("indexrel");
75                 indexRelation.addLocalObserver(o);
76                 indexRelation.addRemoteObserver(o);
77             } catch (Exception JavaDoc e) {
78                 log.service("" + e + " retrying later");
79                 return;
80             }
81             observer = o;
82         }
83     }
84
85     /**
86      * Returns the 'successor' or a string. Which means that e.g. after 'zzz' follows 'aaaa'.
87      */

88     public static String JavaDoc successor(String JavaDoc index) {
89         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(index);
90         boolean lowercase = true;
91         for (int i = index.length() - 1 ; i >= 0; i--) {
92             char c = buf.charAt(i);
93             if (c >= 'a' && c <= 'y') {
94                 buf.setCharAt(i, (char) (c + 1));
95                 return buf.toString();
96             } else if (c == 'z') {
97                 buf.setCharAt(i, 'a');
98                 continue;
99             } else if (c >= 'A' && c <= 'Y') {
100                 buf.setCharAt(i, (char) (c + 1));
101                 return buf.toString();
102             } else if (c == 'Z') {
103                 lowercase = false;
104                 buf.setCharAt(i, 'A');
105                 continue;
106             } else if ((int) c < 128) {
107                 buf.setCharAt(i, (char) (c + 1));
108                 return buf.toString();
109             } else {
110                 buf.setCharAt(i, (char) 65);
111                 continue;
112             }
113         }
114
115         if (lowercase) {
116             buf.insert(0, 'a');
117         } else {
118             buf.insert(0, 'A');
119         }
120         return buf.toString();
121     }
122
123
124     /**
125      * Calculates the 'successor' of a roman number, preserving uppercase/lowercase.
126      */

127     protected static String JavaDoc romanSuccessor(String JavaDoc index) {
128         boolean uppercase = index.length() > 0 && Character.isUpperCase(index.charAt(0));
129         String JavaDoc res = RomanTransformer.decimalToRoman(RomanTransformer.romanToDecimal(index) + 1);
130         return uppercase ? res.toUpperCase() : res;
131
132     }
133     /**
134      * Calculates the 'successor' of an index String. Like '7.4.iii' of which the successor is
135      * '7.4.iv'.
136      *
137      * @param index The string to succeed
138      * @param separator Regular expression to split up the string first (e.g. "\\.")
139      * @param joiner String to rejoin it again (e.g. ".")
140      * @param roman Whether to consider roman numbers
141      */

142     protected static String JavaDoc successor(String JavaDoc index, String JavaDoc separator, String JavaDoc joiner, boolean roman) {
143         String JavaDoc[] split = index.split(separator);
144         String JavaDoc postfix = split[split.length - 1];
145         if (RomanTransformer.NUMERIC.matcher(postfix).matches()) {
146             postfix = "" + (Integer.parseInt(postfix) + 1);
147         } else {
148             if (! roman || ! RomanTransformer.ROMAN.matcher(postfix).matches()) {
149                 postfix = successor(postfix);
150             } else {
151                 postfix = romanSuccessor(postfix);
152             }
153         }
154         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
155         for (int i = 0; i < split.length - 1; i++) {
156             buf.append(split[i]);
157             buf.append(joiner);
158         }
159         buf.append(postfix);
160         return buf.toString();
161     }
162
163     private static Parameter[] INDEX_ARGS = new Parameter[] {
164         Parameter.CLOUD,
165         new Parameter("root", Node.class, false),
166         new Parameter("separator", String JavaDoc.class, "\\."),
167         new Parameter("joiner", String JavaDoc.class, "."),
168         new Parameter("roman", Boolean JavaDoc.class, Boolean.TRUE),
169         new Parameter("role", String JavaDoc.class, "index")
170     };
171
172     private static Parameter[] MOVE_ARGS = new Parameter[] {
173         Parameter.CLOUD,
174         new Parameter("root", Node.class, false),
175         new Parameter("newroot", Node.class, false)
176     };
177
178     /**
179      * calculates a key for the cache
180      */

181     private static String JavaDoc getKey(final Node node, final Parameters parameters) {
182         Node root = (Node) parameters.get("root");
183         final String JavaDoc role = (String JavaDoc) parameters.get("role");
184         final String JavaDoc join = (String JavaDoc) parameters.get("joiner");
185         final String JavaDoc separator = (String JavaDoc) parameters.get("separator");
186         final boolean roman = ((Boolean JavaDoc) parameters.get("roman")).booleanValue();
187         return "" + node.getNumber() + "/" + (root == null ? "NULL" : "" + root.getNumber()) + "/" + role + "/" + join + "/" + separator + "/" + roman;
188     }
189
190
191     protected static class Stack extends ArrayList {
192         public void push(Object JavaDoc o) {
193             add(0, o);
194         }
195         public Object JavaDoc pull() {
196             return remove(0);
197         }
198     }
199
200     protected static NodeFunction index = new NodeFunction("index", INDEX_ARGS, ReturnType.STRING) {
201             {
202                 setDescription("Calculates the index of a node, using the surrounding 'indexrels'");
203             }
204
205             /**
206              * complete bridge version of {@link #getFunctionValue}
207              */

208             public Object JavaDoc getFunctionValue(final Node node, final Parameters parameters) {
209                 Node root = (Node) parameters.get("root");
210                 final String JavaDoc role = (String JavaDoc) parameters.get("role");
211                 final String JavaDoc join = (String JavaDoc) parameters.get("joiner");
212                 final String JavaDoc separator = (String JavaDoc) parameters.get("separator");
213                 final Pattern indexPattern = Pattern.compile("(.+)" + separator + "(.+)");
214                 final boolean roman = ((Boolean JavaDoc) parameters.get("roman")).booleanValue();
215
216                 final String JavaDoc key = getKey(node, parameters);
217
218                 initObserver();
219                 String JavaDoc result = (String JavaDoc) indexCache.get(key);
220                 if (result != null) {
221                     if (log.isDebugEnabled()) {
222                         log.debug("Found index '" + result + "' for node " + node.getNumber() + " from cache (key " + key + ")");
223                     }
224                     return result;
225                 }
226                 log.debug("Determining index for node " + node.getNumber() + " with role " + role);
227
228                 final NodeManager nm = node.getNodeManager();
229
230                 // now we have to determine the path from node to root.
231

232                 GrowingTreeList tree = new GrowingTreeList(Queries.createNodeQuery(node), 10, nm, role, "source");
233                 NodeQuery template = tree.getTemplate();
234                 if (root != null) {
235                     StepField sf = template.addField(role + ".root");
236                     template.setConstraint(template.createConstraint(sf, root));
237                 }
238
239                 Stack stack = new Stack();
240                 TreeIterator it = tree.treeIterator();
241                 int depth = it.currentDepth();
242                 while (it.hasNext()) {
243                     Node n = it.nextNode();
244                     if (log.isDebugEnabled()) {
245                         log.debug("Considering at " + it.currentDepth() + "/" + depth + " node " + n.getNodeManager().getName() + " " + n.getNumber());
246                     }
247                     if (it.currentDepth() > depth) {
248                         stack.push(n);
249                         depth = it.currentDepth();
250                     }
251                     if (indexCache.contains(getKey(n, parameters))) {
252                         if (log.isDebugEnabled()) {
253                             log.debug("Index for " + n.getNumber() + " is known already!, breaking");
254                         }
255                         break;
256                     }
257
258                     if (it.currentDepth() < depth) {
259                         break;
260                     }
261                     //if (root == null) root = n.getNodeValue(role + ".root");
262
if (root != null && n.getNumber() == root.getNumber()) break;
263                 }
264
265                 if (stack.isEmpty()) {
266                     log.debug("Stack is empty, no root found, returning ''");
267                     indexCache.put(key, "");
268                     return "";
269                 }
270
271                 if (log.isDebugEnabled()) {
272                     log.debug("Now constructing index-number with " + stack.size() + " nodes on stack");
273                 }
274                 Node n = (Node) stack.pull(); // this is root, or at least _its_ index is known
275
StringBuffer JavaDoc buf;
276                 if (! n.equals(node)) {
277                     buf = new StringBuffer JavaDoc(n.getFunctionValue("index", parameters).toString());
278                 } else {
279                     buf = new StringBuffer JavaDoc();
280                 }
281                 String JavaDoc j = buf.length() == 0 ? "" : join;
282                 OUTER:
283                 while(! stack.isEmpty()) {
284                     Node search = (Node) stack.pull();
285                     NodeQuery q = Queries.createRelatedNodesQuery(n, nm, role, "destination");
286                     StepField sf = q.addField(role + ".pos");
287                     q.addSortOrder(sf, SortOrder.ORDER_ASCENDING);
288                     q.addField(role + ".index");
289                     if (log.isDebugEnabled()) {
290                         log.debug("Executing " + q.toSql() + " to search " + search.getNumber());
291                     }
292                     String JavaDoc index = null;
293                     NodeIterator ni = q.getCloud().getList(q).nodeIterator();
294                     boolean doRoman = roman;
295                     while(ni.hasNext()) {
296                         Node clusterFound = ni.nextNode();
297                         Node found = clusterFound.getNodeValue(q.getNodeStep().getAlias());
298                         String JavaDoc i = clusterFound.getStringValue(role + ".index");
299                         if (i == null || i.equals("")) i = index;
300                         if (i == null) i = "1";
301                         log.debug("Found index " + i);
302                         Matcher matcher = indexPattern.matcher(i);
303                         if (matcher.matches()) {
304                             buf = new StringBuffer JavaDoc(matcher.group(1));
305                             i = matcher.group(2);
306                             log.debug("matched " + indexPattern + " --> " + i);
307                         }
308                         doRoman = doRoman && RomanTransformer.ROMAN.matcher(i).matches();
309
310                         if (found.getNumber() == search.getNumber()) {
311                             log.debug("found sibling");
312                             // found!
313
buf.append(j).append(i);
314                             j = join;
315                             n = found;
316                             continue OUTER;
317                         }
318                         index = successor(i, separator, join, doRoman);
319                         log.debug("Considering next sibling, index is now " + index);
320                         // can as well cache this one too.
321
indexCache.put(getKey(found, parameters), buf.toString() + j + i);
322                     }
323                     // not found
324
buf.append(j).append("???");
325                     break;
326                 }
327                 String JavaDoc r = buf.toString();
328                 log.debug("Found '" + r + "' for " + key);
329                 indexCache.put(key, r);
330                 return r;
331             }
332         };
333     {
334         addFunction(index);
335     }
336
337
338     public static void main(String JavaDoc argv[]) {
339
340         CloudContext cc = ContextProvider.getDefaultCloudContext();
341         Cloud cloud = cc.getCloud("mmbase", "class", null);
342         Node node = cloud.getNode(argv[0]);
343         Node root = null;
344         if (argv.length > 1) root = cloud.getNode(argv[1]);
345         Parameters params = index.createParameters();
346         params.set("root", root);
347         params.set("roman", Boolean.TRUE);
348         System.out.println("" + index.getFunctionValue(node, params));
349
350
351
352     }
353
354 }
355
Popular Tags