package de.lmu.ifi.dbs.elki.data; /* This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures Copyright (C) 2012 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ import java.util.regex.Pattern; import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation; /** * A HierarchicalClassLabel is a ClassLabel to reflect a hierarchical structure * of classes. * * @author Arthur Zimek * * @apiviz.composedOf Comparable */ public class HierarchicalClassLabel extends ClassLabel { /** * The default separator pattern, a point ('.'). */ public static final Pattern DEFAULT_SEPARATOR = Pattern.compile("\\."); /** * The default separator, a point ('.'). */ public static final String DEFAULT_SEPARATOR_STRING = "."; /** * Type information. */ public static final SimpleTypeInformation TYPE = new SimpleTypeInformation(HierarchicalClassLabel.class); /** * Holds the Pattern to separate different levels parsing input. */ private Pattern separatorPattern; /** * A String to separate different levels in a String representation of this * HierarchicalClassLabel. */ private String separatorString; /** * Holds the names on the different levels. */ private Comparable[] levelwiseNames; /** * Constructs a hierarchical class label from the given name, using the given * Pattern to match separators of different levels in the given name, and * setting the given separator-String to separate different levels in String * representations of this HierarchicalClassLabel. * * @param name a String describing a hierarchical class label * @param regex a Pattern to match separators of different levels in the given * name * @param separator a separator String to separate different levels in the * String-representation of this HierarchicalClassLabel */ public HierarchicalClassLabel(String name, Pattern regex, String separator) { super(); this.separatorPattern = regex; this.separatorString = separator; String[] levelwiseStrings = separatorPattern.split(name); this.levelwiseNames = new Comparable[levelwiseStrings.length]; for (int i = 0; i < levelwiseStrings.length; i++) { try { levelwiseNames[i] = Integer.valueOf(levelwiseStrings[i]); } catch (NumberFormatException e) { levelwiseNames[i] = levelwiseStrings[i]; } } } /** * Constructs a hierarchical class label from the given name. Different levels * are supposed to be separated by points ('.'), as defined by * {@link #DEFAULT_SEPARATOR DEFAULT_SEPARATOR}. Also, in a * String-representation of this HierarchicalClassLabel, different levels get * separated by '.'. * * @param label a String describing a hierarchical class label */ public HierarchicalClassLabel(String label) { this(label, DEFAULT_SEPARATOR, DEFAULT_SEPARATOR_STRING); } /** * Compares two HierarchicalClassLabels. Names at higher levels are compared * first. Names at a lower level are compared only if their parent-names are * equal. Names at a level are tried to be compared as integer values. If this * does not succeed, both names are compared as Strings. * * {@inheritDoc} */ @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public int compareTo(ClassLabel o) { HierarchicalClassLabel h = (HierarchicalClassLabel) o; for (int i = 0; i < this.levelwiseNames.length && i < h.levelwiseNames.length; i++) { int comp = 0; try { Comparable first = this.levelwiseNames[i]; Comparable second = h.levelwiseNames[i]; comp = first.compareTo(second); } catch (RuntimeException e) { String h1 = (String) (this.levelwiseNames[i] instanceof Integer ? this.levelwiseNames[i].toString() : this.levelwiseNames[i]); String h2 = (String) (h.levelwiseNames[i] instanceof Integer ? h.levelwiseNames[i].toString() : h.levelwiseNames[i]); comp = h1.compareTo(h2); } if (comp != 0) { return comp; } } return (this.levelwiseNames.length < h.levelwiseNames.length) ? -1 : ((this.levelwiseNames.length == h.levelwiseNames.length) ? 0 : 1); } /** * The length of the hierarchy of names. * * @return length of the hierarchy of names */ public int depth() { return levelwiseNames.length - 1; } /** * Returns the name at the given level as a String. * * @param level the level to return the name at * @return the name at the given level as a String */ public String getNameAt(int level) { return this.levelwiseNames[level] instanceof Integer ? this.levelwiseNames[level].toString() : (String) this.levelwiseNames[level]; } /** * Returns a String representation of this HierarchicalClassLabel using * {@link #separatorString separatorString} to separate levels. * * {@inheritDoc} */ @Override public String toString() { return toString(levelwiseNames.length); } /** * Provides a String representation of this ClassLabel comprising only the * first level levels. * * @param level the lowest level to include in the String representation. * @return a String representation of this ClassLabel comprising only the * first level levels */ public String toString(int level) { if (level > levelwiseNames.length) { throw new IllegalArgumentException("Specified level exceeds depth of hierarchy."); } StringBuilder name = new StringBuilder(); for (int i = 0; i < level; i++) { name.append(this.getNameAt(i)); if (i < level - 1) { name.append(this.separatorString); } } return name.toString(); } /** * Factory class. * * @author Erich Schubert * * @apiviz.has HierarchicalClassLabel - - «creates» * @apiviz.stereotype factory */ public static class Factory extends ClassLabel.Factory { @Override public HierarchicalClassLabel makeFromString(String lbl) { lbl = lbl.intern(); HierarchicalClassLabel l = existing.get(lbl); if (l == null) { l = new HierarchicalClassLabel(lbl); existing.put(lbl, l); } return l; } @Override public SimpleTypeInformation getTypeInformation() { return TYPE; } } }