/** * Copyright 2013 Tommi S.E. Laukkanen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.bubblecloud.ilves.util; import org.bubblecloud.ilves.exception.SiteException; import java.util.*; /** * Utility class for parsing navigation tree. * * @author Tommi S.E. Laukkanen */ public class NavigationTreeParser { /** The list of root nodes in the navigation map. */ public static final String ROOTS = "roots"; /** * Parses tree navigation to map from format: "a;#aa;#ab;##aba;b;c;#ca". * * List of roots can be accessed with ROOTS key from the resulting map. * * @param tree the navigation tree presented as String * * @return map of parent node name and child node name lists. */ public static Map<String, List<String>> parse(final String tree) { final Map<String, List<String>> treeMap = new HashMap<String, List<String>>(); final LinkedList<String> parentStack = new LinkedList<String>(); final String[] nodes = tree.split(";"); String parent = ROOTS; String previousNodeName = null; for (int i = 0; i < nodes.length; i++) { final int depth = parentStack.size(); final String node = nodes[i]; final int nodeDepth = node.lastIndexOf('#') + 2; final String nodeName = node.substring(nodeDepth - 1); if (depth + 1 == nodeDepth) { // Child of current parent } else if (depth + 2 == nodeDepth) { // Child of previous node if (previousNodeName == null) { throw new SiteException("Invalid navigation tree hierarchy: " + tree); } parentStack.push(parent); parent = previousNodeName; } else if (depth >= nodeDepth) { // Child of some earlier parent in stack while (parentStack.size() + 1 != nodeDepth) { parent = parentStack.pop(); } } else { throw new SiteException("Invalid navigation tree hierarchy: " + tree); } if (!treeMap.containsKey(parent)) { treeMap.put(parent, new ArrayList<String>()); } treeMap.get(parent).add(nodeName); previousNodeName = nodeName; } return treeMap; } /** * Formats the navigation string as map. * @param navigationMap the navigation map * @return the navigation map as string */ public static String format(final Map<String, List<String>> navigationMap) { final StringBuilder tree = new StringBuilder(); format(ROOTS, -1, navigationMap, tree); return tree.toString(); } private static void format(final String parent, final int depth, final Map<String, List<String>> navigationMap, final StringBuilder tree) { if (depth >= 0) { if (tree.length() > 0) { tree.append(';'); } for (int i = 0; i < depth; i++) { tree.append('#'); } tree.append(parent); } if (!navigationMap.containsKey(parent)) { return; } for (final String child : navigationMap.get(parent)) { format(child, depth + 1, navigationMap, tree); } } }