/* Copyright (C) 2002 Univ. of Massachusetts Amherst, Computer Science Dept. This file is part of "MALLET" (MAchine Learning for LanguagE Toolkit). http://www.cs.umass.edu/~mccallum/mallet This software is provided under the terms of the Common Public License, version 1.0, as published by http://www.opensource.org. For further information, see the file `LICENSE' included with this distribution. */ /** @author Andrew McCallum <a href="mailto:mccallum@cs.umass.edu">mccallum@cs.umass.edu</a> */ package edu.nd.nina.util; import java.util.HashMap; import java.util.HashSet; public class PropertyList{ protected PropertyList next; protected String key; public static PropertyList add(String key, Object value, PropertyList rest) { assert (key != null); return new ObjectProperty(key, value, rest); } public static PropertyList add(String key, String value, PropertyList rest) { assert (key != null); return new ObjectProperty(key, value, rest); } public static PropertyList add(String key, double value, PropertyList rest) { assert (key != null); return new NumericProperty(key, value, rest); } public static PropertyList remove(String key, PropertyList rest) { assert (key != null); return new ObjectProperty(key, null, rest); } public Object lookupObject(String key) { if (this.key.equals(key)) { if (this instanceof ObjectProperty) return ((ObjectProperty) this).value; else if (this instanceof NumericProperty) return new Double(((NumericProperty) this).value); else throw new IllegalStateException( "Unrecognitized PropertyList entry."); } else if (this.next == null) { return null; } else { return next.lookupObject(key); } } public double lookupNumber(String key) { if (this.key.equals(key)) { if (this instanceof NumericProperty) return ((NumericProperty) this).value; else if (this instanceof ObjectProperty) { Object obj = ((ObjectProperty) this).value; if (obj == null) return 0; // xxx Remove these? Use might ask for numericIterator expecting // to get these (and not!) if (obj instanceof Double) return ((Double) obj).doubleValue(); if (obj instanceof Integer) return ((Double) obj).intValue(); if (obj instanceof Float) return ((Double) obj).floatValue(); if (obj instanceof Short) return ((Double) obj).shortValue(); if (obj instanceof Long) return ((Double) obj).longValue(); // xxx? throw new IllegalStateException // ("Property is not numeric."); return 0; } else throw new IllegalStateException( "Unrecognitized PropertyList entry."); } else if (this.next == null) { return 0; } else { return next.lookupNumber(key); } } public boolean hasProperty(String key) { if (this.key.equals(key)) { if (this instanceof ObjectProperty && ((ObjectProperty) this).value == null) return false; else return true; } else if (this.next == null) { return false; } else { return next.hasProperty(key); } } public Iterator iterator() { return new Iterator(this); } public static PropertyList sumDuplicateKeyValues(PropertyList pl) { return sumDuplicateKeyValues(pl, false); } // culotta 2/02/04: to increment counts of properties values. public static PropertyList sumDuplicateKeyValues(PropertyList pl, boolean ignoreZeros) { if (!(pl instanceof NumericProperty)) throw new IllegalArgumentException( "PropertyList must be Numeric to sum values"); HashMap<String, Double> key2value = new HashMap<String, Double>(); while (pl.iterator().hasNext()) { pl.iterator().nextProperty (); String key = pl.iterator().getKey(); double val = pl.iterator().getNumericValue(); Double storedValue = (Double) key2value.get(key); if (storedValue == null) key2value.put(key, new Double(val)); else // sum stored value with current value key2value.put(key, new Double(storedValue.doubleValue() + val)); } PropertyList ret = null; java.util.Iterator<String> hashIter = key2value.keySet().iterator(); while (hashIter.hasNext()) { // create new property list String key = (String) hashIter.next(); double val = ((Double) key2value.get(key)).doubleValue(); if (ignoreZeros && val == 0.0) continue; ret = PropertyList.add(key, val, ret); } return ret; } public Iterator numericIterator() { return new NumericIterator(this); } public Iterator objectIterator() { return new ObjectIterator(this); } protected PropertyList() { throw new IllegalArgumentException("Zero args constructor not allowed."); } protected PropertyList(String key, PropertyList rest) { this.key = key; this.next = rest; } public void print() { if (this instanceof NumericProperty) System.out.println(this.key.toString() + "=" + ((NumericProperty) this).value); else if (this instanceof ObjectProperty) System.out.println(this.key.toString() + "=" + ((ObjectProperty) this).value); else throw new IllegalArgumentException("Unrecognized PropertyList type"); if (this.next != null) this.next.print(); } public int size() { PropertyList pl = this; int size = 1; while (pl.next != null) { pl = pl.next; size++; } return size; } private static class NumericProperty extends PropertyList { protected double value; public NumericProperty(String key, double value, PropertyList rest) { super(key, rest); this.value = value; } } private static class ObjectProperty extends PropertyList { protected Object value; public ObjectProperty(String key, Object value, PropertyList rest) { super(key, rest); this.value = value; } } public class Iterator implements java.util.Iterator<PropertyList> { PropertyList property, nextProperty; HashSet<String> deletedKeys = null; boolean nextCalled = false; boolean returnNumeric = true; boolean returnObject = true; public Iterator(PropertyList pl) { property = findReturnablePropertyAtOrAfter(pl); if (property == null) nextProperty = null; else nextProperty = findReturnablePropertyAtOrAfter(property.next); } private PropertyList findReturnablePropertyAtOrAfter( PropertyList property) { while (property != null) { if (property instanceof NumericProperty && returnNumeric) { if (((NumericProperty) property).value == 0.0) { if (deletedKeys == null) deletedKeys = new HashSet<String>(); deletedKeys.add(property.key); property = property.next; } else break; } else if (property instanceof ObjectProperty && returnObject) { if (((ObjectProperty) property).value == null) { if (deletedKeys == null) deletedKeys = new HashSet<String>(); deletedKeys.add(property.key); property = property.next; } else break; } else throw new IllegalStateException( "Unrecognized property type " + property.getClass().getName()); } return property; } public boolean hasNext() { return ((nextCalled && nextProperty != null) || (!nextCalled && property != null)); } public boolean isNumeric() { return (property instanceof NumericProperty); } public double getNumericValue() { return ((NumericProperty) property).value; } public Object getObjectValue() { return ((ObjectProperty) property).value; } public String getKey() { return property.key; } public PropertyList nextProperty() { if (nextCalled) { property = nextProperty; nextProperty = findReturnablePropertyAtOrAfter(property.next); } else nextCalled = true; return property; } public PropertyList next() { return nextProperty(); } public void remove() { throw new UnsupportedOperationException(); } } public class NumericIterator extends Iterator { public NumericIterator(PropertyList pl) { super(pl); this.returnObject = false; } } public class ObjectIterator extends Iterator { public ObjectIterator(PropertyList pl) { super(pl); this.returnNumeric = false; } } // for fast merging of PropertLists // gmann 8/14/6 public PropertyList last() { if (next == null) { return this; } else return next.last(); } public PropertyList append(PropertyList nextPl) throws UnsupportedOperationException { if (this.next != null) { throw new UnsupportedOperationException( "PropertyList.java: Cannot append to middle of a list\n"); } this.next = nextPl; return last(); } }