/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.enterprise.admin.servermgmt.stringsubs.impl.algorithm; import java.util.logging.Level; import java.util.logging.Logger; import com.sun.enterprise.admin.servermgmt.SLogger; import com.sun.enterprise.universal.i18n.LocalStringsImpl; /** * This class implements the operation of a radix tree. A radix tree * is a specialized set data structure based on the tree/trie that is * used to store a set of strings. The key for nodes of a radix tree * are labeled with one or more characters rather than only a single * characters. */ class RadixTree { private static final Logger _logger = SLogger.getLogger(); private static final LocalStringsImpl _strings = new LocalStringsImpl(RadixTree.class); // Reference to root node. private RadixTreeNode _rootNode; /** * Construct {@link RadixTree} with default root node. */ public RadixTree() { // Creating root node. _rootNode = new RadixTreeNode("", null); } /** * Insert a new entry in tree with the given key, value pair. * <p> * <b>NOTE:</b> * <li> * If user tries to insert the duplicate key than the * value of that key will be updated. * </li> * <li> * Insertion of node having empty or null key is not allowed. * </li> * </p> * <br/> * @param key The input key. * @param value The value that need to be stored corresponding * to the given key. * @return <code>true</code> if key inserted successfully.<br/> * <code>false</code> if insertion failed. */ public void insert(String key, String value) { if (key == null || key.isEmpty()) { throw new IllegalArgumentException(_strings.get("errorInEmptyNullKeyInstertion")); } char[] inputChars = key.toCharArray(); int noOfMatchedChars = 0; RadixTreeNode node = _rootNode; RadixTreeNode newNode = null; int keyLength = inputChars.length; OUTER_LOOP : while (noOfMatchedChars < keyLength) { String nodeKey = node.getKey(); int i = 0; int maxLoop = nodeKey.length() > (keyLength - noOfMatchedChars) ? keyLength - noOfMatchedChars : nodeKey.length(); for (; i < maxLoop ; i++) { if (nodeKey.charAt(i) != inputChars[noOfMatchedChars]) { // e.g new key/value : successive/successive // before, // |-node (key: successful, value: successful) // | |-node_childs... //after... // |-newNode (key: success, value: "") // | |-node (key: ful, value: successful) // | | |-node_childs... // | |-secondNewNode (key: ive, value: successive) newNode = new RadixTreeNode(nodeKey.substring(0, i), null); RadixTreeNode parentNode = node.getParentNode(); parentNode.removeChildNode(node); parentNode.addChildNode(newNode); node.setKey(nodeKey.substring(i)); newNode.addChildNode(node); newNode.addChildNode(new RadixTreeNode(key.substring(noOfMatchedChars), value)); break OUTER_LOOP; } noOfMatchedChars++; } // If the given key is smaller than the matched node key if (nodeKey.length() > maxLoop) { // e.g new key/value : acid/acid // before, // |-node (key: acidic, value: acidic) // | |-node_childs... //after... // |-newNode (key: acid, value: acid) // | |-node (key: ic, value: acidic) // | | |-node_childs... newNode = new RadixTreeNode(nodeKey.substring(0, i), value); RadixTreeNode parentNode = node.getParentNode(); parentNode.removeChildNode(node); parentNode.addChildNode(newNode); node.setKey(nodeKey.substring(i)); newNode.addChildNode(node); break; } if (noOfMatchedChars == keyLength) { if (node.getValue() != null && !node.getValue().isEmpty()) { _logger.log(Level.INFO, SLogger.CHANGE_IN_VALUE, new Object[] {node.getValue(), value}); } node.setValue(value); break; } RadixTreeNode matchedNode = node.getChildNode(inputChars[noOfMatchedChars]); // Add as a child node if no match found. if (matchedNode == null) { node.addChildNode(new RadixTreeNode(key.substring(noOfMatchedChars), value)); break; } node = matchedNode; } } /** * Return's the root node helps to traverse the tree. * * @return Root node of tree. */ RadixTreeNode getRootNode() { return _rootNode; } }