// Copyright 2003-2005, FreeHEP.
package hep.graphics.heprep.util;
import hep.graphics.heprep.HepRepAttDef;
import hep.graphics.heprep.HepRepAttValue;
import hep.graphics.heprep.HepRepAttribute;
import hep.graphics.heprep.HepRepConverter;
import hep.graphics.heprep.HepRepInstance;
import hep.graphics.heprep.HepRepInstanceTree;
import hep.graphics.heprep.HepRepIterator;
import hep.graphics.heprep.HepRepProvider;
import hep.graphics.heprep.HepRepType;
import hep.graphics.heprep.ref.DefaultHepRepIterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.openide.util.Lookup;
/**
*
* @author Mark Donszelmann
*
* @version $Id: HepRepUtil.java 8584 2006-08-10 23:06:37Z duns $
*/
public class HepRepUtil {
// Static class, not to be instantiated
private HepRepUtil() {
}
/**
* Print statements in equal methods
* @return true if print should happen
*/
public static boolean debug() {
return false;
}
/**
* Decodes a String into a Double. The string can have the following formats:
* <pre>
* 0ds<number> : where number is the DoubleToLongBits encoding in special format
* 0d0x<number>: where number is the DoubleToLongBits encoding in hex
* 0d#<number> : where number is the DoubleToLongBits encoding in hex
* 0d<number> : where number is the DoubleToLongBits encoding in decimal
* 0x<number> : where number is the Hex encoding of a Long
* <number> : where number contains (.Ee) and is parsed into a Double
* <number> : where number contains no (.Ee) and is parsed into a Long
* </pre>
* @param s number as string
* @return decoded double
*/
public static double decodeNumber(String s) {
double d = 0;
try {
if (s.startsWith("0ds")) {
d = Double.longBitsToDouble(decodeSpecial(s.substring(3)));
} else if (s.startsWith("0d0x")) {
d = Double.longBitsToDouble(decodeHex(s.substring(4)));
} else if (s.startsWith("0d#")) {
d = Double.longBitsToDouble(decodeHex(s.substring(3)));
} else if (s.startsWith("0d")) {
d = Double.longBitsToDouble(decodeHex(s.substring(2)));
} else if (s.startsWith("0x")) {
d = Long.decode(s).doubleValue();
} else if ((s.indexOf(".") < 0) && (s.indexOf("E") < 0) && (s.indexOf("e") < 0)) {
d = Long.decode(s).doubleValue();
} else {
d = Double.valueOf(s).doubleValue();
}
} catch (NumberFormatException nfe) {
System.err.println("decodeNumber: "+s);
nfe.printStackTrace();
throw nfe;
}
return d;
}
private static char[] hexChars = new char[16];
/**
* Decodes a non-negative Hex number into a long
* @param s number as string
* @return decoded number
* @throws NumberFormatException in case string is malformed
*/
public static long decodeHex(String s) throws NumberFormatException {
long result = 0;
int len = Math.min(hexChars.length, s.length());
s.getChars(0, len, hexChars, 0);
for (int i=0; i<len; i++) {
result = result << 4;
char c = hexChars[i];
if ((48 <= c) && (c <= 57)) {
// 0..9
result += (c-48);
} else if ((65 <= c) && (c <= 70)) {
result += (c-65+10);
} else if ((97 <= c) && (c <= 102)) {
result += (c-97+10);
} else {
throw new NumberFormatException("Not a hex number: "+s);
}
}
return result;
}
// NOTE this should be less than 16
private static char[] specialChars = new char[16];
/**
* Decodes a non-negative Special number into a long
* @param s special number string
* @return long number
* @throws NumberFormatException in case string is malformed
*/
public static long decodeSpecial(String s) throws NumberFormatException {
long result = 0;
int len = Math.min(specialChars.length, s.length());
s.getChars(0, len, specialChars, 0);
for (int i=0; i<len; i++) {
result = result << 6;
char c = specialChars[i];
if ((48 <= c) && (c <= 57)) {
// 0..9
result += (c-48);
} else if ((65 <= c) && (c <= 90)) {
// A..Z
result += (c-65+10);
} else if ((97 <= c) && (c <= 124)) {
// a..z,{,|
result += (c-97+10+26);
} else {
throw new NumberFormatException("Not a special number: "+s);
}
}
return result;
}
/**
* Encodes a number into a special
* @param d number
* @return special number string
*/
public static String encodeSpecial(long d) {
for (int i=10; i>=0; i--) { // 64 bits in 6 bit parts, 11 parts
int c = (int)(d & 0x3f); // 6 bits
if (c < 10) {
specialChars[i] = (char)('0'+c);
} else if (c < 10+26) {
specialChars[i] = (char)('A'+c-10);
} else if (c < 10+26+26+2) {
specialChars[i] = (char)('a'+c-10-26);
} else {
System.err.println("encodeSpecial: this looks bad.");
}
d = d >>> 6;
}
return new String(specialChars, 0, 11);
}
/**
* Some tests for encoding and decoding
* @param args ignored
*/
public static void main(String[] args) {
System.out.println(""+Long.toHexString(decodeSpecial("0123456789ABCDEF")));
System.out.println(""+Long.toHexString(decodeSpecial("GHIJKLMNOPQRSTUV")));
System.out.println(""+Long.toHexString(decodeSpecial("WXYZabcdefghijkl")));
System.out.println(""+encodeSpecial(0x59a7a29aabb2dbafL));
System.out.println(""+Long.toHexString(decodeSpecial("mnopqrstuvwxyz{|")));
System.out.println(""+encodeSpecial(0x5db7e39ebbf3dfbfL));
}
/**
* copy all attributes (defs and values) from node src to node dst
* @param src
* @param dst
* @throws CloneNotSupportedException
*/
public static void copyAttributes(HepRepAttribute src, HepRepAttribute dst) throws CloneNotSupportedException {
// BUG FIX. Do something special for layers, because these do not end
// up in the normal iteration.
HepRepAttValue layerAtt = src.getAttValueFromNode("layer");
if (layerAtt!=null) {
dst.addAttValue(layerAtt.copy());
}
// copy all att values
for (Iterator i=src.getAttValuesFromNode().iterator(); i.hasNext(); ) {
HepRepAttValue value = (HepRepAttValue)i.next();
dst.addAttValue(value.copy());
}
}
/**
* Look for hierarchical type name in Collection (Set) of types, checking its subtypes.
* @param types collection of types to search
* @param name type to look for
* @return HepRepType or null if not found
*/
public static HepRepType getType(Collection/*<HepRepType>*/ types, String name) {
// remove leading slash
if (name.startsWith("/")) name = name.substring(1);
// split name
int slash = name.indexOf("/");
String rest = "";
if (slash >= 0) {
rest = name.substring(slash+1);
name = name.substring(0,slash);
}
// search
for (Iterator i=types.iterator(); i.hasNext(); ) {
HepRepType type = (HepRepType)i.next();
if (type.getName().equals(name)) {
if (rest.equals("")) return type;
return getType(type.getTypeList(), rest);
}
}
return null;
}
/**
* Returns an iterator which walks the full list of instances within a list of HepRepInstanceTrees
* limited by a list of layers, set of types and augmented by double passes for frame iteration.
*
* @param instanceTrees HepRepInstanceTrees to be iterated over
* @param layers list of layers to iteratate over or null.
* @param types set of types for which iteration returns instances
* @param iterateFrames iterate separately over each layer for frames
* @return HepRepIterator
*/
public static HepRepIterator getInstances(List/*<HepRepInstanceTree>*/ instanceTrees, List/*<String>*/ layers,
Set/*<Object>*/ types, boolean iterateFrames) {
return new DefaultHepRepIterator(instanceTrees, layers, types, iterateFrames);
}
/**
* iterates two iterations in order
* @param first first iterator
* @param second second iterator
* @return combined iterator
*/
public static Iterator iterator(Iterator first, Iterator second) {
final Iterator f = (first != null) ? first : Collections.EMPTY_LIST.iterator();
final Iterator s = (second != null) ? second : Collections.EMPTY_LIST.iterator();
return new Iterator() {
public boolean hasNext() {
return f.hasNext() || s.hasNext();
}
public Object next() {
if (f.hasNext()) {
return f.next();
}
return s.next();
}
public void remove() {
// for now, if implemented think about the semantics of the last entry in the first enum
throw new UnsupportedOperationException();
}
};
}
/**
* Finds the first HepRepProvider which converts object.
* Return null if not found.
* @param registry registry to look for provider
* @param object object to convert
* @return provider to convert object
*/
public static HepRepProvider getHepRepProvider(Lookup registry, Object object) {
Collection allConverters = registry.lookup(new Lookup.Template(HepRepProvider.class)).allInstances();
for (Iterator i = allConverters.iterator(); i.hasNext(); ) {
HepRepProvider converter = (HepRepProvider)i.next();
if (converter.canConvert(object)) {
return converter;
}
}
return null;
}
/**
* Finds the first HepRepConverter which handles object. For backwards compatibility.
* Return null if not found.
* @param registry registry to look for provider
* @param cls class of object to convert
* @return converter to convert object
* @deprecated use getHepRepProvider().
*/
public static HepRepConverter getHepRepConverter(Lookup registry, Class cls) {
Collection allConverters = registry.lookup(new Lookup.Template(HepRepConverter.class)).allInstances();
for (Iterator i = allConverters.iterator(); i.hasNext(); ) {
HepRepConverter converter = (HepRepConverter)i.next();
if (converter.canHandle(cls)) {
return converter;
}
}
return null;
}
/**
* Returns the Unit from the extra info. Normally the string up to the first colon, or null if extra is null.
* @param attDef attribute definition
* @return unit string
*/
public static String getUnit(HepRepAttDef attDef) {
String extra = attDef.getExtra();
if (extra == null) return null;
int colon = extra.indexOf(":");
return (colon >= 0) ? extra.substring(0, colon) : extra;
}
/**
* Returns a Set of all layernames which are used, and possibly visible and pickable.
*
* @param instanceTree instance tree to look in
* @param checkVisible look only in visible instances
* @param checkPickable look only in pickable instances
* @return set of layers
*/
public static Set/*<String>*/ getAllLayerNames(HepRepInstanceTree instanceTree, boolean checkVisible, boolean checkPickable) {
Set names = new HashSet();
List trees = new ArrayList();
trees.add(instanceTree);
HepRepIterator iterator = HepRepUtil.getInstances(trees, null, null, false);
while (iterator.hasNext()) {
HepRepInstance instance = iterator.nextInstance();
if (checkPickable && !instance.getAttValue("ispickable").getBoolean()) continue;
if (!checkVisible || instance.getAttValue("visibility").getBoolean()) {
names.add(instance.getAttValue("layer").getString());
}
}
return names;
}
}