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 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 getName() { 41 return "IndexNumberCache"; 42 } 43 public String 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 machine, String number, String builder, String ctype) { 61 return nodeChanged(machine, number, builder, ctype); 62 } 63 public boolean nodeLocalChanged(String machine, String number, String builder, String ctype) { 64 return nodeChanged(machine, number, builder, ctype); 65 } 66 public boolean nodeChanged(String machine, String number, String builder, String ctype) { 67 log.info("Received change " + machine + "/" + number + "/" + builder + "/" + ctype); 68 indexCache.clear(); return true; 70 } 71 72 73 }; 74 MMObjectBuilder indexRelation = MMBase.getMMBase().getBuilder("indexrel"); 75 indexRelation.addLocalObserver(o); 76 indexRelation.addRemoteObserver(o); 77 } catch (Exception e) { 78 log.service("" + e + " retrying later"); 79 return; 80 } 81 observer = o; 82 } 83 } 84 85 88 public static String successor(String index) { 89 StringBuffer buf = new StringBuffer (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 127 protected static String romanSuccessor(String index) { 128 boolean uppercase = index.length() > 0 && Character.isUpperCase(index.charAt(0)); 129 String res = RomanTransformer.decimalToRoman(RomanTransformer.romanToDecimal(index) + 1); 130 return uppercase ? res.toUpperCase() : res; 131 132 } 133 142 protected static String successor(String index, String separator, String joiner, boolean roman) { 143 String [] split = index.split(separator); 144 String 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 buf = new StringBuffer (); 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 .class, "\\."), 167 new Parameter("joiner", String .class, "."), 168 new Parameter("roman", Boolean .class, Boolean.TRUE), 169 new Parameter("role", String .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 181 private static String getKey(final Node node, final Parameters parameters) { 182 Node root = (Node) parameters.get("root"); 183 final String role = (String ) parameters.get("role"); 184 final String join = (String ) parameters.get("joiner"); 185 final String separator = (String ) parameters.get("separator"); 186 final boolean roman = ((Boolean ) 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 o) { 193 add(0, o); 194 } 195 public Object 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 208 public Object getFunctionValue(final Node node, final Parameters parameters) { 209 Node root = (Node) parameters.get("root"); 210 final String role = (String ) parameters.get("role"); 211 final String join = (String ) parameters.get("joiner"); 212 final String separator = (String ) parameters.get("separator"); 213 final Pattern indexPattern = Pattern.compile("(.+)" + separator + "(.+)"); 214 final boolean roman = ((Boolean ) parameters.get("roman")).booleanValue(); 215 216 final String key = getKey(node, parameters); 217 218 initObserver(); 219 String result = (String ) 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 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 && 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(); StringBuffer buf; 276 if (! n.equals(node)) { 277 buf = new StringBuffer (n.getFunctionValue("index", parameters).toString()); 278 } else { 279 buf = new StringBuffer (); 280 } 281 String 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 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 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 (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 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 indexCache.put(getKey(found, parameters), buf.toString() + j + i); 322 } 323 buf.append(j).append("???"); 325 break; 326 } 327 String 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 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 |