db0ca2b9d4
Reviewed-by: lfoltan, coleenp
176 lines
6.3 KiB
Java
176 lines
6.3 KiB
Java
/*
|
|
* Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package nsk.share;
|
|
|
|
import java.util.*;
|
|
|
|
/**
|
|
* This denotation provides naming and indexing for nodes
|
|
* of a binary, or ternary, or <tt>n</tt>-ary tree.
|
|
*
|
|
* <p>Here, <tt>n</tt> would be the length of a symbols
|
|
* string used as an alphabeth for nodes naming. For a
|
|
* binary tree, <tt>n=2</tt>, and an aplhabeth could be
|
|
* the <tt>"LR"</tt> string. This implies the following
|
|
* naming for tree nodes:
|
|
* <pre>
|
|
* (empty)
|
|
* / \
|
|
* L R
|
|
* / \ / \
|
|
* LL LR RL RR
|
|
* / \ / \ / \ / \
|
|
* </pre>
|
|
*
|
|
* <p>Anyway, the tree root node is named with the empty
|
|
* string <tt>""</tt> and is indexed with 2-zeroes array
|
|
* <tt>{0,0}</tt>.
|
|
*
|
|
* <p>Index for a tree node is 2-elements <tt>int[]</tt>
|
|
* array. The 1st element is the node's level in a tree.
|
|
* The 2nd element is the item's number among all nodes of
|
|
* that level; provided that node items are enumerated from
|
|
* <tt>0</tt> to <tt>n</tt><sup>level</sup><tt>-1</tt>.
|
|
* Given a level, lexicographic order is assumed for the
|
|
* nodes of the same level.
|
|
*
|
|
* <p>For example: given the above sample tree, the node
|
|
* <tt>"L"</tt> has the index <tt>{1,0}</tt>, while the
|
|
* node <tt>"RL"</tt> has the index <tt>{2,2}</tt>.
|
|
*
|
|
* <p>In general case, ordering of characters used for nodes
|
|
* naming is implied by the given alphabeth. This may differ
|
|
* from the ``natural'' ordering. For example, if alphabeth
|
|
* is <tt>"ZYX...CBA"</tt>, then ordering for nodes would be
|
|
* opposite to ``natural''.
|
|
*/
|
|
public class TreeNodesDenotation extends Denotation {
|
|
/**
|
|
* Symbols to denote tree nodes.
|
|
*
|
|
* @see #TreeNodeDenotation(String)
|
|
*/
|
|
private String alphabeth;
|
|
|
|
/**
|
|
* Standard denotation for a binary tree; alphabeth
|
|
* is <tt>"LR"</tt>.
|
|
*
|
|
* @see #TreeNodesDenotation(String)
|
|
*/
|
|
public TreeNodesDenotation() {
|
|
this("LR");
|
|
}
|
|
|
|
/**
|
|
* Denotation for nodes of a tree.
|
|
*
|
|
* <p>Each tree node is marked with a string of symbols
|
|
* from the given <tt>alphabeth</tt>. A string length
|
|
* equals to the node's level. The root node is always
|
|
* denoted with the empty string.
|
|
*
|
|
* <p>For example, an <tt>alphabeth</tt> for a binary
|
|
* tree could be <tt>"LR"</tt>, or <tt>"01"</tt>, or
|
|
* any 2-symbols string. However, <tt>"lL"</tt> or
|
|
* <tt>"rR"</tt> would be illegal because of collision
|
|
* between upper- and lower- case letters.
|
|
*
|
|
* <p>In general case, it is illegal for <tt>alphabeth</tt>
|
|
* to contain two or several copies of the same symbol.
|
|
* This constructor deems lower- and upper-case variants
|
|
* of the same letter are the same symbol.
|
|
*
|
|
* @throws IllegalArgumentException If the <tt>alphabeth</tt>
|
|
* looks illegal.
|
|
*/
|
|
public TreeNodesDenotation(String alphabeth) {
|
|
if (alphabeth.length() == 0)
|
|
throw new IllegalArgumentException("empty alphabeth");
|
|
// Check for lower- to upper- case collision:
|
|
this.alphabeth = alphabeth.toUpperCase();
|
|
int length = this.alphabeth.length();
|
|
Set<Character> pool = new HashSet<Character>(); // still empty
|
|
for (int i=0; i<length; i++)
|
|
pool.add(Character.valueOf(this.alphabeth.charAt(i)));
|
|
if (pool.size() != length)
|
|
throw new IllegalArgumentException("collision: " + alphabeth);
|
|
}
|
|
|
|
/**
|
|
* Check if the <tt>name</tt> is legal, and return the
|
|
* numeric index for the tree node denoted by the given
|
|
* <tt>name</tt>.
|
|
*
|
|
* @throws IllegalArgumentException If the <tt>name</tt>
|
|
* is illegal.
|
|
*/
|
|
public int[] indexFor(String name) {
|
|
int level = name.length();
|
|
int factor = alphabeth.length();
|
|
long item = 0;
|
|
for (int i=0; i<level; i++) {
|
|
char symbol = Character.toUpperCase(name.charAt(i));
|
|
int position = alphabeth.indexOf(symbol);
|
|
if (position < 0)
|
|
throw new IllegalArgumentException("unknown symbol: " + name);
|
|
item = item*factor + position;
|
|
if (item < 0 || item > Integer.MAX_VALUE)
|
|
throw new IllegalArgumentException("too long name: " + name);
|
|
};
|
|
int[] index = new int [] { level, (int)item };
|
|
return index;
|
|
}
|
|
|
|
/**
|
|
* Check if the <tt>index[]</tt> is legal, and return
|
|
* a symbolic name for the tree node denoted by the
|
|
* given <tt>index[]</tt>.
|
|
*
|
|
* @throws IllegalArgumentException If the <tt>index[]</tt>
|
|
* is illegal.
|
|
*/
|
|
public String nameFor(int[] index) {
|
|
if (index.length != 2)
|
|
throw new IllegalArgumentException(
|
|
"index dimention: " + index.length);
|
|
StringBuffer name = new StringBuffer(); // still empty
|
|
int level = index[0];
|
|
int item = index[1];
|
|
if (level < 0 || item < 0)
|
|
throw new IllegalArgumentException(
|
|
"negative index: " + level + ", " + item);
|
|
int factor = alphabeth.length();
|
|
for (int i=0; i<level; i++) {
|
|
int k = item % factor;
|
|
name.append(alphabeth.charAt(k));
|
|
item = item / factor;
|
|
};
|
|
if (item != 0)
|
|
throw new IllegalArgumentException(
|
|
"out of range: {"+ index[0] + "," + index[1] + "}");
|
|
return new String(name.reverse());
|
|
}
|
|
}
|