/*
 *    Copyright 2012 Jason Miller
 *
 * 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 jj.http.uri;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

class StaticNode extends TrieNode {
	
	Map<String, TrieNode> children;
	int keyLength = 1;
	
	void doAddChild(Route route) {
		children = children == null ? new LinkedHashMap<String, TrieNode>(4, 0.75f) : children;
		TrieNode nextNode;
		if (route.currentChar() == PATH_SEPARATOR_CHAR) {
			if (terminal) {
				throw new AssertionError("terminal!");
			}
			
			nextNode = children.get(PATH_SEPARATOR_STRING);
			if (nextNode == null) {
				nextNode = new SeparatorNode(PATH_SEPARATOR_CHAR);
				children.put(PATH_SEPARATOR_STRING, nextNode);
			}
		} else if (route.currentChar() == EXTENSION_SEPARATOR_CHAR && !route.hasRemainingSegments()) {
			if (terminal) {
				throw new AssertionError("terminal!");
			}
			nextNode = children.get(EXTENSION_SEPARATOR_STRING);
			if (nextNode == null) {
				nextNode = new SeparatorNode(EXTENSION_SEPARATOR_CHAR);
				children.put(EXTENSION_SEPARATOR_STRING, nextNode);
			}
			
		} else {

			String current = String.valueOf(route.currentChar());
			nextNode = children.get(current);
			if (nextNode == null) {
				nextNode = new StaticNode();
				children.put(current, nextNode);
			}
		}
		nextNode.addRoute(route.advanceIndex());
	}
	
	StaticNode mergeUp(StringBuilder accumulator) {
		if (children != null && children.size() == 1 && goal == null) {
			String key = children.keySet().iterator().next();
			if (!PATH_SEPARATOR_STRING.equals(key) && !EXTENSION_SEPARATOR_STRING.equals(key)) {
				StaticNode node = (StaticNode)children.get(key);
				accumulator.append(key);
				return node.mergeUp(accumulator);
			}
		}
		
		return this;
	}
	
	@Override
	void doCompress() {
		if (children != null) {
			if (children.size() == 1) {
				String key = children.keySet().iterator().next();
				if (!PATH_SEPARATOR_STRING.equals(key) && !EXTENSION_SEPARATOR_STRING.equals(key)) {
					StringBuilder accumulator = new StringBuilder(key);
					StaticNode node = (StaticNode)children.remove(key);
					TrieNode newNode = node.mergeUp(accumulator);
					keyLength = accumulator.length();
					newNode.compress();
					children = Collections.singletonMap(accumulator.toString(), newNode);
				} else {
					children = Collections.singletonMap(key, children.get(key));
					children.get(key).compress();
				}
			} else {
				for (TrieNode child : children.values()) {
					child.compress();
				}
				children = Collections.unmodifiableMap(children);
			}
		}
	}
	
	@Override
	boolean findGoal(RouteFinderContext context, String uri, int index) {
		if (uri.length() == index) {
			if (goal != null) {
				context.setGoal(goal);
				return true;
			}
			return false;
		}
		
		if (children != null && uri.length() >= index + keyLength) {
			String current = uri.substring(index, index + keyLength);
			if (children.containsKey(current)) {
				return children.get(current).findGoal(context, uri, index + keyLength);
			}
		}
		
		return false;
	}
	
	@Override
	void describeChildren(int indent, StringBuilder sb) {
		if (children != null) {
			for (String key : children.keySet()) {
				addIndentation(indent, sb.append("\n")).append(key).append(" = ");
				children.get(key).describe(indent, sb);
			}
		}
	}
	
	@Override
	StringBuilder describe(int indent, StringBuilder sb) {
		sb.append(getClass().getSimpleName()).append(" - keylength: ").append(keyLength).append(", goal: ").append(goal);
		describeChildren(indent + 2, sb);
		return sb;
	}
}
@@ -937,20 +937,27 @@
 Node;%0A%09%09
-if (
+char cur = 
 route.cu
@@ -963,24 +963,35 @@
 urrentChar()
+;%0A%09%09if (cur
  == PATH_SEP
@@ -1005,435 +1005,258 @@
 CHAR
-) %7B
+ %7C%7C
 %0A%09%09%09
-if (terminal) %7B%0A%09%09%09%09throw new AssertionError(%22terminal!%22);%0A%09%09%09%7D%0A%09%09%09%0A%09%09%09nextNode = children.get(PATH_SEPARATOR_STRING);%0A%09%09%09if (nextNode == null) %7B%0A%09%09%09%09nextNode = new SeparatorNode(PATH_SEPARATOR_CHAR);%0A%09%09%09%09children.put(PATH_SEPARATOR_STRING, nextNode);%0A%09%09%09%7D%0A%09%09%7D else if (route.currentChar() == EXTENSION_SEPARATOR_CHAR && !route.hasRemainingSegments()) %7B%0A%09%09%09if (terminal) %7B%0A%09%09%09%09throw new AssertionError(%22terminal!%22)
+(cur== EXTENSION_SEPARATOR_CHAR && !route.hasRemainingSegments())) %7B%0A%09%09%09assert !terminal : %22terminal node has additional trailing nodes!%22;%0A%09%09%09%0A%09%09%09String key = cur == PATH_SEPARATOR_CHAR ? PATH_SEPARATOR_STRING : EXTENSION_SEPARATOR_STRING
 ;%0A%09%09%09
-%7D
 %0A%09%09%09
@@ -1283,34 +1283,11 @@
 get(
-EXTENSION_SEPARATOR_STRING
+key
 );%0A%09
@@ -1349,78 +1349,34 @@
 ode(
-EXTENSION_SEPARATOR_CHAR);%0A%09%09%09%09children.put(EXTENSION_SEPARATOR_STRING
+cur);%0A%09%09%09%09children.put(key
 , ne
@@ -1389,20 +1389,16 @@
 );%0A%09%09%09%7D%0A
-%09%09%09%0A
 %09%09%7D else
@@ -1400,17 +1400,16 @@
  else %7B%0A
-%0A
 %09%09%09Strin
@@ -1435,35 +1435,19 @@
 valueOf(
-route.currentChar()
+cur
 );%0A%09%09%09ne
@@ -1811,85 +1811,47 @@
 if (
-!PATH_SEPARATOR_STRING.equals(key) && !EXTENSION_SEPARATOR_STRING.equals(key)
+children.get(key) instanceof StaticNode
 ) %7B%0A
@@ -2157,85 +2157,47 @@
 if (
-!PATH_SEPARATOR_STRING.equals(key) && !EXTENSION_SEPARATOR_STRING.equals(key)
+children.get(key) instanceof StaticNode
 ) %7B%0A

efficiency improvements