/* * Copyright 2012 Takao Nakaguchi * * 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.trie4j.patricia.multilayer.labeltrie; import java.util.Arrays; import org.trie4j.Node; import org.trie4j.NodeVisitor; import org.trie4j.patricia.multilayer.node.LabelTrieNode; import org.trie4j.util.Pair; public class LabelNode implements Node{ public static final char[] emptyChars = {}; public LabelNode(char[] letters) { this.letters = letters; } public LabelNode(char[] letters, LabelNode parent) { this.letters = letters; this.parent = parent; } public char[] getLetters() { return letters; } public void setLetters(char[] letters) { this.letters = letters; } @Override public boolean isTerminate() { return false; } public LabelNode getParent() { return parent; } public void setParent(LabelNode parent) { this.parent = parent; } public LabelNode[] getChildren() { return null; } public void setChildren(LabelNode[] children) { throw new UnsupportedOperationException(); } public LabelNode getChild(char c){ return null; } public LabelNode insertChild(int childIndex, char[] letters, int offset){ if(this.letters == null){ this.letters = Arrays.copyOfRange(letters, offset, letters.length); return this; } //hello$ //h -> insert to children (a) //h$ -> insert to children (b) //hat -> h, at, ello$ (c) //hat$ -> h, at$, ello$ (d) //hello -> hello$ (e) //hello$ -> hello$ (f) //helloworld$ -> hello$, helloworld$ (g) //helloworld (h) int i = 0; int lettersRest = letters.length - offset; int thisLettersLength = this.letters.length; int n = Math.min(lettersRest, thisLettersLength); int c = 0; while(i < n && (c = letters[i + offset] - this.letters[i]) == 0) i++; if(i == n){ if(lettersRest == thisLettersLength){ return this; } if(lettersRest < thisLettersLength){ LabelNode parent = new InternalLabelNode( Arrays.copyOf(this.letters, i) , this.parent , new LabelNode[]{this}); if(this.parent != null){ this.parent.getChildren()[childIndex] = parent; } this.letters = Arrays.copyOfRange(this.letters, i, this.letters.length); this.parent = parent; return parent; } if(getChildren() != null){ int index = 0; int end = getChildren().length; if(end > 16){ int start = 0; while(start < end){ index = (start + end) / 2; LabelNode child = getChildren()[index]; c = letters[i + offset] - child.letters[0]; if(c == 0){ return child.insertChild(index, letters, i + offset); } if(c < 0){ end = index; } else if(start == index){ index = end; break; } else{ start = index; } } } else{ for(index = 0; index < end; index++){ LabelNode child = getChildren()[index]; c = letters[i + offset] - child.letters[0]; if(c < 0) break; if(c == 0){ return child.insertChild(index, letters, i + offset); } } } LabelNode child = new InternalLabelNode( Arrays.copyOfRange(letters, i + offset, letters.length) , this, null); addChild(index, child); return child; } else{ LabelNode child = new InternalLabelNode( Arrays.copyOfRange(letters, i + offset, letters.length) , this, null ); setChildren(new LabelNode[]{child}); return child; } } char[] newParentsLetter = Arrays.copyOfRange(this.letters, 0, i); char[] newThisLetter = Arrays.copyOfRange(this.letters, i, this.letters.length); char[] newChildsLetter = Arrays.copyOfRange(letters, i + offset, letters.length); LabelNode[] newParentsChildren = new LabelNode[2]; LabelNode newParent = new InternalLabelNode( newParentsLetter, this.parent, newParentsChildren ); LabelNode newChild = new InternalLabelNode( newChildsLetter, newParent, null ); if(newThisLetter[0] < newChildsLetter[0]){ newParentsChildren[0] = this; newParentsChildren[1] = newChild; } else{ newParentsChildren[0] = newChild; newParentsChildren[1] = this; } this.letters = newThisLetter; if(this.parent != null){ this.parent.getChildren()[childIndex] = newParent; } this.parent = newParent; return newChild; } boolean contains(char[] letters, int offset){ int rest = letters.length - offset; int tll = this.letters.length; if(tll > rest) return false; for(int i = 0; i < tll; i++){ if(this.letters[i] != letters[i + offset]) return false; } if(tll == rest){ return true; } offset += tll; char c = letters[offset]; LabelNode n = getChild(c); if(n != null){ return n.contains(letters, offset); } return false; } public Pair<Integer, Boolean> containsBottomup(char[] letters, int offset){ int rest = letters.length - offset; int n = this.letters.length; int ttlm1 = n - 1; if(n > rest) return Pair.create(0, false); for(int i = 0; i < n; i++){ if(this.letters[ttlm1 - i] != letters[offset]){ return Pair.create(i, false); } offset++; } if(n == rest){ return Pair.create(n, true); } if(parent != null){ Pair<Integer, Boolean> ret = parent.containsBottomup(letters, offset); return Pair.create(ret.getFirst() + n, ret.getSecond()); } else{ return Pair.create(n, true); } } public void pargeChildren(){ } public void visit(NodeVisitor visitor, int nest){ visitor.visit(this, nest); } public void addChild(int index, LabelNode n){ throw new UnsupportedOperationException(); } public void addReferer(LabelTrieNode l) { throw new UnsupportedOperationException(); } public void removeReferer(LabelTrieNode l) { } private char[] letters; private LabelNode parent; }