0001: /*
0002: * This program is free software; you can redistribute it and/or modify
0003: * it under the terms of the GNU General Public License as published by
0004: * the Free Software Foundation; either version 2 of the License, or
0005: * (at your option) any later version.
0006: *
0007: * This program is distributed in the hope that it will be useful,
0008: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0009: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0010: * GNU General Public License for more details.
0011: *
0012: * You should have received a copy of the GNU General Public License
0013: * along with this program; if not, write to the Free Software
0014: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
0015: */
0016:
0017: /*
0018: * Attribute.java
0019: * Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
0020: *
0021: */
0022:
0023: package weka.core;
0024:
0025: import java.io.Serializable;
0026: import java.text.ParseException;
0027: import java.text.SimpleDateFormat;
0028: import java.util.Date;
0029: import java.util.Enumeration;
0030: import java.util.Hashtable;
0031: import java.util.Properties;
0032: import java.io.StreamTokenizer;
0033: import java.io.StringReader;
0034: import java.io.IOException;
0035:
0036: /**
0037: * Class for handling an attribute. Once an attribute has been created,
0038: * it can't be changed. <p>
0039: *
0040: * The following attribute types are supported:
0041: * <ul>
0042: * <li> numeric: <br/>
0043: * This type of attribute represents a floating-point number.
0044: * </li>
0045: * <li> nominal: <br/>
0046: * This type of attribute represents a fixed set of nominal values.
0047: * </li>
0048: * <li> string: <br/>
0049: * This type of attribute represents a dynamically expanding set of
0050: * nominal values. Usually used in text classification.
0051: * </li>
0052: * <li> date: <br/>
0053: * This type of attribute represents a date, internally represented as
0054: * floating-point number storing the milliseconds since January 1,
0055: * 1970, 00:00:00 GMT. The string representation of the date must be
0056: * <a href="http://www.iso.org/iso/en/prods-services/popstds/datesandtime.html" target="_blank">
0057: * ISO-8601</a> compliant, the default is <code>yyyy-MM-dd'T'HH:mm:ss</code>.
0058: * </li>
0059: * <li> relational: <br/>
0060: * This type of attribute can contain other attributes and is, e.g.,
0061: * used for representing Multi-Instance data. (Multi-Instance data
0062: * consists of a nominal attribute containing the bag-id, then a
0063: * relational attribute with all the attributes of the bag, and
0064: * finally the class attribute.)
0065: * </li>
0066: * </ul>
0067: *
0068: * Typical usage (code from the main() method of this class): <p>
0069: *
0070: * <code>
0071: * ... <br>
0072: *
0073: * // Create numeric attributes "length" and "weight" <br>
0074: * Attribute length = new Attribute("length"); <br>
0075: * Attribute weight = new Attribute("weight"); <br><br>
0076: *
0077: * // Create vector to hold nominal values "first", "second", "third" <br>
0078: * FastVector my_nominal_values = new FastVector(3); <br>
0079: * my_nominal_values.addElement("first"); <br>
0080: * my_nominal_values.addElement("second"); <br>
0081: * my_nominal_values.addElement("third"); <br><br>
0082: *
0083: * // Create nominal attribute "position" <br>
0084: * Attribute position = new Attribute("position", my_nominal_values);<br>
0085: *
0086: * ... <br>
0087: * </code><p>
0088: *
0089: * @author Eibe Frank (eibe@cs.waikato.ac.nz)
0090: * @version $Revision: 1.44 $
0091: */
0092: public class Attribute implements Copyable, Serializable {
0093:
0094: /** for serialization */
0095: static final long serialVersionUID = -742180568732916383L;
0096:
0097: /** Constant set for numeric attributes. */
0098: public static final int NUMERIC = 0;
0099:
0100: /** Constant set for nominal attributes. */
0101: public static final int NOMINAL = 1;
0102:
0103: /** Constant set for attributes with string values. */
0104: public static final int STRING = 2;
0105:
0106: /** Constant set for attributes with date values. */
0107: public static final int DATE = 3;
0108:
0109: /** Constant set for relation-valued attributes. */
0110: public static final int RELATIONAL = 4;
0111:
0112: /** Constant set for symbolic attributes. */
0113: public static final int ORDERING_SYMBOLIC = 0;
0114:
0115: /** Constant set for ordered attributes. */
0116: public static final int ORDERING_ORDERED = 1;
0117:
0118: /** Constant set for modulo-ordered attributes. */
0119: public static final int ORDERING_MODULO = 2;
0120:
0121: /** The keyword used to denote the start of an arff attribute declaration */
0122: public final static String ARFF_ATTRIBUTE = "@attribute";
0123:
0124: /** A keyword used to denote a numeric attribute */
0125: public final static String ARFF_ATTRIBUTE_INTEGER = "integer";
0126:
0127: /** A keyword used to denote a numeric attribute */
0128: public final static String ARFF_ATTRIBUTE_REAL = "real";
0129:
0130: /** A keyword used to denote a numeric attribute */
0131: public final static String ARFF_ATTRIBUTE_NUMERIC = "numeric";
0132:
0133: /** The keyword used to denote a string attribute */
0134: public final static String ARFF_ATTRIBUTE_STRING = "string";
0135:
0136: /** The keyword used to denote a date attribute */
0137: public final static String ARFF_ATTRIBUTE_DATE = "date";
0138:
0139: /** The keyword used to denote a relation-valued attribute */
0140: public final static String ARFF_ATTRIBUTE_RELATIONAL = "relational";
0141:
0142: /** The keyword used to denote the end of the declaration of a subrelation */
0143: public final static String ARFF_END_SUBRELATION = "@end";
0144:
0145: /** Strings longer than this will be stored compressed. */
0146: private static final int STRING_COMPRESS_THRESHOLD = 200;
0147:
0148: /** The attribute's name. */
0149: private/*@ spec_public non_null @*/String m_Name;
0150:
0151: /** The attribute's type. */
0152: private/*@ spec_public @*/int m_Type;
0153: /*@ invariant m_Type == NUMERIC ||
0154: m_Type == DATE ||
0155: m_Type == STRING ||
0156: m_Type == NOMINAL ||
0157: m_Type == RELATIONAL;
0158: */
0159:
0160: /** The attribute's values (if nominal or string). */
0161: private/*@ spec_public @*/FastVector m_Values;
0162:
0163: /** Mapping of values to indices (if nominal or string). */
0164: private Hashtable m_Hashtable;
0165:
0166: /** The header information for a relation-valued attribute. */
0167: private Instances m_Header;
0168:
0169: /** Date format specification for date attributes */
0170: private SimpleDateFormat m_DateFormat;
0171:
0172: /** The attribute's index. */
0173: private/*@ spec_public @*/int m_Index;
0174:
0175: /** The attribute's metadata. */
0176: private ProtectedProperties m_Metadata;
0177:
0178: /** The attribute's ordering. */
0179: private int m_Ordering;
0180:
0181: /** Whether the attribute is regular. */
0182: private boolean m_IsRegular;
0183:
0184: /** Whether the attribute is averagable. */
0185: private boolean m_IsAveragable;
0186:
0187: /** Whether the attribute has a zeropoint. */
0188: private boolean m_HasZeropoint;
0189:
0190: /** The attribute's weight. */
0191: private double m_Weight;
0192:
0193: /** The attribute's lower numeric bound. */
0194: private double m_LowerBound;
0195:
0196: /** Whether the lower bound is open. */
0197: private boolean m_LowerBoundIsOpen;
0198:
0199: /** The attribute's upper numeric bound. */
0200: private double m_UpperBound;
0201:
0202: /** Whether the upper bound is open */
0203: private boolean m_UpperBoundIsOpen;
0204:
0205: /**
0206: * Constructor for a numeric attribute.
0207: *
0208: * @param attributeName the name for the attribute
0209: */
0210: //@ requires attributeName != null;
0211: //@ ensures m_Name == attributeName;
0212: public Attribute(String attributeName) {
0213:
0214: this (attributeName, new ProtectedProperties(new Properties()));
0215: }
0216:
0217: /**
0218: * Constructor for a numeric attribute, where metadata is supplied.
0219: *
0220: * @param attributeName the name for the attribute
0221: * @param metadata the attribute's properties
0222: */
0223: //@ requires attributeName != null;
0224: //@ requires metadata != null;
0225: //@ ensures m_Name == attributeName;
0226: public Attribute(String attributeName, ProtectedProperties metadata) {
0227:
0228: m_Name = attributeName;
0229: m_Index = -1;
0230: m_Values = null;
0231: m_Hashtable = null;
0232: m_Header = null;
0233: m_Type = NUMERIC;
0234: setMetadata(metadata);
0235: }
0236:
0237: /**
0238: * Constructor for a date attribute.
0239: *
0240: * @param attributeName the name for the attribute
0241: * @param dateFormat a string suitable for use with
0242: * SimpleDateFormatter for parsing dates.
0243: */
0244: //@ requires attributeName != null;
0245: //@ requires dateFormat != null;
0246: //@ ensures m_Name == attributeName;
0247: public Attribute(String attributeName, String dateFormat) {
0248:
0249: this (attributeName, dateFormat, new ProtectedProperties(
0250: new Properties()));
0251: }
0252:
0253: /**
0254: * Constructor for a date attribute, where metadata is supplied.
0255: *
0256: * @param attributeName the name for the attribute
0257: * @param dateFormat a string suitable for use with
0258: * SimpleDateFormatter for parsing dates.
0259: * @param metadata the attribute's properties
0260: */
0261: //@ requires attributeName != null;
0262: //@ requires dateFormat != null;
0263: //@ requires metadata != null;
0264: //@ ensures m_Name == attributeName;
0265: public Attribute(String attributeName, String dateFormat,
0266: ProtectedProperties metadata) {
0267:
0268: m_Name = attributeName;
0269: m_Index = -1;
0270: m_Values = null;
0271: m_Hashtable = null;
0272: m_Header = null;
0273: m_Type = DATE;
0274: if (dateFormat != null) {
0275: m_DateFormat = new SimpleDateFormat(dateFormat);
0276: } else {
0277: m_DateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
0278: }
0279: m_DateFormat.setLenient(false);
0280: setMetadata(metadata);
0281: }
0282:
0283: /**
0284: * Constructor for nominal attributes and string attributes.
0285: * If a null vector of attribute values is passed to the method,
0286: * the attribute is assumed to be a string.
0287: *
0288: * @param attributeName the name for the attribute
0289: * @param attributeValues a vector of strings denoting the
0290: * attribute values. Null if the attribute is a string attribute.
0291: */
0292: //@ requires attributeName != null;
0293: //@ ensures m_Name == attributeName;
0294: public Attribute(String attributeName, FastVector attributeValues) {
0295:
0296: this (attributeName, attributeValues, new ProtectedProperties(
0297: new Properties()));
0298: }
0299:
0300: /**
0301: * Constructor for nominal attributes and string attributes, where
0302: * metadata is supplied. If a null vector of attribute values is passed
0303: * to the method, the attribute is assumed to be a string.
0304: *
0305: * @param attributeName the name for the attribute
0306: * @param attributeValues a vector of strings denoting the
0307: * attribute values. Null if the attribute is a string attribute.
0308: * @param metadata the attribute's properties
0309: */
0310: //@ requires attributeName != null;
0311: //@ requires metadata != null;
0312: /*@ ensures m_Name == attributeName;
0313: ensures m_Index == -1;
0314: ensures attributeValues == null && m_Type == STRING
0315: || attributeValues != null && m_Type == NOMINAL
0316: && m_Values.size() == attributeValues.size();
0317: signals (IllegalArgumentException ex)
0318: (* if duplicate strings in attributeValues *);
0319: */
0320: public Attribute(String attributeName, FastVector attributeValues,
0321: ProtectedProperties metadata) {
0322:
0323: m_Name = attributeName;
0324: m_Index = -1;
0325: if (attributeValues == null) {
0326: m_Values = new FastVector();
0327: m_Hashtable = new Hashtable();
0328: m_Header = null;
0329: m_Type = STRING;
0330: } else {
0331: m_Values = new FastVector(attributeValues.size());
0332: m_Hashtable = new Hashtable(attributeValues.size());
0333: m_Header = null;
0334: for (int i = 0; i < attributeValues.size(); i++) {
0335: Object store = attributeValues.elementAt(i);
0336: if (((String) store).length() > STRING_COMPRESS_THRESHOLD) {
0337: try {
0338: store = new SerializedObject(attributeValues
0339: .elementAt(i), true);
0340: } catch (Exception ex) {
0341: System.err
0342: .println("Couldn't compress nominal attribute value -"
0343: + " storing uncompressed.");
0344: }
0345: }
0346: if (m_Hashtable.containsKey(store)) {
0347: throw new IllegalArgumentException(
0348: "A nominal attribute (" + attributeName
0349: + ") cannot"
0350: + " have duplicate labels ("
0351: + store + ").");
0352: }
0353: m_Values.addElement(store);
0354: m_Hashtable.put(store, new Integer(i));
0355: }
0356: m_Type = NOMINAL;
0357: }
0358: setMetadata(metadata);
0359: }
0360:
0361: /**
0362: * Constructor for relation-valued attributes.
0363: *
0364: * @param attributeName the name for the attribute
0365: * @param header an Instances object specifying the header of the relation.
0366: */
0367: public Attribute(String attributeName, Instances header) {
0368:
0369: this (attributeName, header, new ProtectedProperties(
0370: new Properties()));
0371: }
0372:
0373: /**
0374: * Constructor for relation-valued attributes.
0375: *
0376: * @param attributeName the name for the attribute
0377: * @param header an Instances object specifying the header of the relation.
0378: * @param metadata the attribute's properties
0379: */
0380: public Attribute(String attributeName, Instances header,
0381: ProtectedProperties metadata) {
0382:
0383: if (header.numInstances() > 0) {
0384: throw new IllegalArgumentException(
0385: "Header for relation-valued "
0386: + "attribute should not contain "
0387: + "any instances");
0388: }
0389: m_Name = attributeName;
0390: m_Index = -1;
0391: m_Values = new FastVector();
0392: m_Hashtable = new Hashtable();
0393: m_Header = header;
0394: m_Type = RELATIONAL;
0395: setMetadata(metadata);
0396: }
0397:
0398: /**
0399: * Produces a shallow copy of this attribute.
0400: *
0401: * @return a copy of this attribute with the same index
0402: */
0403: //@ also ensures \result instanceof Attribute;
0404: public/*@ pure non_null @*/Object copy() {
0405:
0406: Attribute copy = new Attribute(m_Name);
0407:
0408: copy.m_Index = m_Index;
0409: copy.m_Type = m_Type;
0410: copy.m_Values = m_Values;
0411: copy.m_Hashtable = m_Hashtable;
0412: copy.m_DateFormat = m_DateFormat;
0413: copy.m_Header = m_Header;
0414: copy.setMetadata(m_Metadata);
0415:
0416: return copy;
0417: }
0418:
0419: /**
0420: * Returns an enumeration of all the attribute's values if the
0421: * attribute is nominal, string, or relation-valued, null otherwise.
0422: *
0423: * @return enumeration of all the attribute's values
0424: */
0425: public final/*@ pure @*/Enumeration enumerateValues() {
0426:
0427: if (isNominal() || isString()) {
0428: final Enumeration ee = m_Values.elements();
0429: return new Enumeration() {
0430: public boolean hasMoreElements() {
0431: return ee.hasMoreElements();
0432: }
0433:
0434: public Object nextElement() {
0435: Object oo = ee.nextElement();
0436: if (oo instanceof SerializedObject) {
0437: return ((SerializedObject) oo).getObject();
0438: } else {
0439: return oo;
0440: }
0441: }
0442: };
0443: }
0444: return null;
0445: }
0446:
0447: /**
0448: * Tests if given attribute is equal to this attribute.
0449: *
0450: * @param other the Object to be compared to this attribute
0451: * @return true if the given attribute is equal to this attribute
0452: */
0453: public final/*@ pure @*/boolean equals(Object other) {
0454:
0455: if ((other == null)
0456: || !(other.getClass().equals(this .getClass()))) {
0457: return false;
0458: }
0459: Attribute att = (Attribute) other;
0460: if (!m_Name.equals(att.m_Name)) {
0461: return false;
0462: }
0463: if (isNominal() && att.isNominal()) {
0464: if (m_Values.size() != att.m_Values.size()) {
0465: return false;
0466: }
0467: for (int i = 0; i < m_Values.size(); i++) {
0468: if (!m_Values.elementAt(i).equals(
0469: att.m_Values.elementAt(i))) {
0470: return false;
0471: }
0472: }
0473: return true;
0474: }
0475: if (isRelationValued() && att.isRelationValued()) {
0476: if (!m_Header.equalHeaders(att.m_Header)) {
0477: return false;
0478: }
0479: return true;
0480: }
0481: return (type() == att.type());
0482: }
0483:
0484: /**
0485: * Returns the index of this attribute.
0486: *
0487: * @return the index of this attribute
0488: */
0489: //@ ensures \result == m_Index;
0490: public final/*@ pure @*/int index() {
0491:
0492: return m_Index;
0493: }
0494:
0495: /**
0496: * Returns the index of a given attribute value. (The index of
0497: * the first occurence of this value.)
0498: *
0499: * @param value the value for which the index is to be returned
0500: * @return the index of the given attribute value if attribute
0501: * is nominal or a string, -1 if it is not or the value
0502: * can't be found
0503: */
0504: public final int indexOfValue(String value) {
0505:
0506: if (!isNominal() && !isString())
0507: return -1;
0508: Object store = value;
0509: if (value.length() > STRING_COMPRESS_THRESHOLD) {
0510: try {
0511: store = new SerializedObject(value, true);
0512: } catch (Exception ex) {
0513: System.err
0514: .println("Couldn't compress string attribute value -"
0515: + " searching uncompressed.");
0516: }
0517: }
0518: Integer val = (Integer) m_Hashtable.get(store);
0519: if (val == null)
0520: return -1;
0521: else
0522: return val.intValue();
0523: }
0524:
0525: /**
0526: * Test if the attribute is nominal.
0527: *
0528: * @return true if the attribute is nominal
0529: */
0530: //@ ensures \result <==> (m_Type == NOMINAL);
0531: public final/*@ pure @*/boolean isNominal() {
0532:
0533: return (m_Type == NOMINAL);
0534: }
0535:
0536: /**
0537: * Tests if the attribute is numeric.
0538: *
0539: * @return true if the attribute is numeric
0540: */
0541: //@ ensures \result <==> ((m_Type == NUMERIC) || (m_Type == DATE));
0542: public final/*@ pure @*/boolean isNumeric() {
0543:
0544: return ((m_Type == NUMERIC) || (m_Type == DATE));
0545: }
0546:
0547: /**
0548: * Tests if the attribute is relation valued.
0549: *
0550: * @return true if the attribute is relation valued
0551: */
0552: //@ ensures \result <==> (m_Type == RELATIONAL);
0553: public final/*@ pure @*/boolean isRelationValued() {
0554:
0555: return (m_Type == RELATIONAL);
0556: }
0557:
0558: /**
0559: * Tests if the attribute is a string.
0560: *
0561: * @return true if the attribute is a string
0562: */
0563: //@ ensures \result <==> (m_Type == STRING);
0564: public final/*@ pure @*/boolean isString() {
0565:
0566: return (m_Type == STRING);
0567: }
0568:
0569: /**
0570: * Tests if the attribute is a date type.
0571: *
0572: * @return true if the attribute is a date type
0573: */
0574: //@ ensures \result <==> (m_Type == DATE);
0575: public final/*@ pure @*/boolean isDate() {
0576:
0577: return (m_Type == DATE);
0578: }
0579:
0580: /**
0581: * Returns the attribute's name.
0582: *
0583: * @return the attribute's name as a string
0584: */
0585: //@ ensures \result == m_Name;
0586: public final/*@ pure @*/String name() {
0587:
0588: return m_Name;
0589: }
0590:
0591: /**
0592: * Returns the number of attribute values. Returns 0 for
0593: * attributes that are not either nominal, string, or
0594: * relation-valued.
0595: *
0596: * @return the number of attribute values
0597: */
0598: public final/*@ pure @*/int numValues() {
0599:
0600: if (!isNominal() && !isString() && !isRelationValued()) {
0601: return 0;
0602: } else {
0603: return m_Values.size();
0604: }
0605: }
0606:
0607: /**
0608: * Returns a description of this attribute in ARFF format. Quotes
0609: * strings if they contain whitespace characters, or if they
0610: * are a question mark.
0611: *
0612: * @return a description of this attribute as a string
0613: */
0614: public final String toString() {
0615:
0616: StringBuffer text = new StringBuffer();
0617:
0618: text.append(ARFF_ATTRIBUTE).append(" ").append(
0619: Utils.quote(m_Name)).append(" ");
0620: switch (m_Type) {
0621: case NOMINAL:
0622: text.append('{');
0623: Enumeration enu = enumerateValues();
0624: while (enu.hasMoreElements()) {
0625: text.append(Utils.quote((String) enu.nextElement()));
0626: if (enu.hasMoreElements())
0627: text.append(',');
0628: }
0629: text.append('}');
0630: break;
0631: case NUMERIC:
0632: text.append(ARFF_ATTRIBUTE_NUMERIC);
0633: break;
0634: case STRING:
0635: text.append(ARFF_ATTRIBUTE_STRING);
0636: break;
0637: case DATE:
0638: text.append(ARFF_ATTRIBUTE_DATE).append(" ").append(
0639: Utils.quote(m_DateFormat.toPattern()));
0640: break;
0641: case RELATIONAL:
0642: text.append(ARFF_ATTRIBUTE_RELATIONAL).append("\n");
0643: Enumeration enm = m_Header.enumerateAttributes();
0644: while (enm.hasMoreElements()) {
0645: text.append(enm.nextElement()).append("\n");
0646: }
0647: text.append(ARFF_END_SUBRELATION).append(" ").append(
0648: Utils.quote(m_Name));
0649: break;
0650: default:
0651: text.append("UNKNOWN");
0652: break;
0653: }
0654: return text.toString();
0655: }
0656:
0657: /**
0658: * Returns the attribute's type as an integer.
0659: *
0660: * @return the attribute's type.
0661: */
0662: //@ ensures \result == m_Type;
0663: public final/*@ pure @*/int type() {
0664:
0665: return m_Type;
0666: }
0667:
0668: /**
0669: * Returns the Date format pattern in case this attribute is of type DATE,
0670: * otherwise an empty string.
0671: *
0672: * @return the date format pattern
0673: * @see SimpleDateFormat
0674: */
0675: public final String getDateFormat() {
0676: if (isDate())
0677: return m_DateFormat.toPattern();
0678: else
0679: return "";
0680: }
0681:
0682: /**
0683: * Returns a value of a nominal or string attribute. Returns an
0684: * empty string if the attribute is neither a string nor a nominal
0685: * attribute.
0686: *
0687: * @param valIndex the value's index
0688: * @return the attribute's value as a string
0689: */
0690: public final/*@ non_null pure @*/String value(int valIndex) {
0691:
0692: if (!isNominal() && !isString()) {
0693: return "";
0694: } else {
0695: Object val = m_Values.elementAt(valIndex);
0696:
0697: // If we're storing strings compressed, uncompress it.
0698: if (val instanceof SerializedObject) {
0699: val = ((SerializedObject) val).getObject();
0700: }
0701: return (String) val;
0702: }
0703: }
0704:
0705: /**
0706: * Returns the header info for a relation-valued attribute,
0707: * null if the attribute is not relation-valued.
0708: *
0709: * @return the attribute's value as an Instances object
0710: */
0711: public final/*@ non_null pure @*/Instances relation() {
0712:
0713: if (!isRelationValued()) {
0714: return null;
0715: } else {
0716: return m_Header;
0717: }
0718: }
0719:
0720: /**
0721: * Returns a value of a relation-valued attribute. Returns
0722: * null if the attribute is not relation-valued.
0723: *
0724: * @param valIndex the value's index
0725: * @return the attribute's value as an Instances object
0726: */
0727: public final/*@ non_null pure @*/Instances relation(int valIndex) {
0728:
0729: if (!isRelationValued()) {
0730: return null;
0731: } else {
0732: return (Instances) m_Values.elementAt(valIndex);
0733: }
0734: }
0735:
0736: /**
0737: * Constructor for a numeric attribute with a particular index.
0738: *
0739: * @param attributeName the name for the attribute
0740: * @param index the attribute's index
0741: */
0742: //@ requires attributeName != null;
0743: //@ requires index >= 0;
0744: //@ ensures m_Name == attributeName;
0745: //@ ensures m_Index == index;
0746: public Attribute(String attributeName, int index) {
0747:
0748: this (attributeName);
0749: m_Index = index;
0750: }
0751:
0752: /**
0753: * Constructor for date attributes with a particular index.
0754: *
0755: * @param attributeName the name for the attribute
0756: * @param dateFormat a string suitable for use with
0757: * SimpleDateFormatter for parsing dates. Null for a default format
0758: * string.
0759: * @param index the attribute's index
0760: */
0761: //@ requires attributeName != null;
0762: //@ requires index >= 0;
0763: //@ ensures m_Name == attributeName;
0764: //@ ensures m_Index == index;
0765: public Attribute(String attributeName, String dateFormat, int index) {
0766:
0767: this (attributeName, dateFormat);
0768: m_Index = index;
0769: }
0770:
0771: /**
0772: * Constructor for nominal attributes and string attributes with
0773: * a particular index.
0774: * If a null vector of attribute values is passed to the method,
0775: * the attribute is assumed to be a string.
0776: *
0777: * @param attributeName the name for the attribute
0778: * @param attributeValues a vector of strings denoting the attribute values.
0779: * Null if the attribute is a string attribute.
0780: * @param index the attribute's index
0781: */
0782: //@ requires attributeName != null;
0783: //@ requires index >= 0;
0784: //@ ensures m_Name == attributeName;
0785: //@ ensures m_Index == index;
0786: public Attribute(String attributeName, FastVector attributeValues,
0787: int index) {
0788:
0789: this (attributeName, attributeValues);
0790: m_Index = index;
0791: }
0792:
0793: /**
0794: * Constructor for a relation-valued attribute with a particular index.
0795: *
0796: * @param attributeName the name for the attribute
0797: * @param header the header information for this attribute
0798: * @param index the attribute's index
0799: */
0800: //@ requires attributeName != null;
0801: //@ requires index >= 0;
0802: //@ ensures m_Name == attributeName;
0803: //@ ensures m_Index == index;
0804: public Attribute(String attributeName, Instances header, int index) {
0805:
0806: this (attributeName, header);
0807: m_Index = index;
0808: }
0809:
0810: /**
0811: * Adds a string value to the list of valid strings for attributes
0812: * of type STRING and returns the index of the string.
0813: *
0814: * @param value The string value to add
0815: * @return the index assigned to the string, or -1 if the attribute is not
0816: * of type Attribute.STRING
0817: */
0818: /*@ requires value != null;
0819: ensures isString() && 0 <= \result && \result < m_Values.size() ||
0820: ! isString() && \result == -1;
0821: */
0822: public int addStringValue(String value) {
0823:
0824: if (!isString()) {
0825: return -1;
0826: }
0827: Object store = value;
0828:
0829: if (value.length() > STRING_COMPRESS_THRESHOLD) {
0830: try {
0831: store = new SerializedObject(value, true);
0832: } catch (Exception ex) {
0833: System.err
0834: .println("Couldn't compress string attribute value -"
0835: + " storing uncompressed.");
0836: }
0837: }
0838: Integer index = (Integer) m_Hashtable.get(store);
0839: if (index != null) {
0840: return index.intValue();
0841: } else {
0842: int intIndex = m_Values.size();
0843: m_Values.addElement(store);
0844: m_Hashtable.put(store, new Integer(intIndex));
0845: return intIndex;
0846: }
0847: }
0848:
0849: /**
0850: * Adds a string value to the list of valid strings for attributes
0851: * of type STRING and returns the index of the string. This method is
0852: * more efficient than addStringValue(String) for long strings.
0853: *
0854: * @param src The Attribute containing the string value to add.
0855: * @param index the index of the string value in the source attribute.
0856: * @return the index assigned to the string, or -1 if the attribute is not
0857: * of type Attribute.STRING
0858: */
0859: /*@ requires src != null;
0860: requires 0 <= index && index < src.m_Values.size();
0861: ensures isString() && 0 <= \result && \result < m_Values.size() ||
0862: ! isString() && \result == -1;
0863: */
0864: public int addStringValue(Attribute src, int index) {
0865:
0866: if (!isString()) {
0867: return -1;
0868: }
0869: Object store = src.m_Values.elementAt(index);
0870: Integer oldIndex = (Integer) m_Hashtable.get(store);
0871: if (oldIndex != null) {
0872: return oldIndex.intValue();
0873: } else {
0874: int intIndex = m_Values.size();
0875: m_Values.addElement(store);
0876: m_Hashtable.put(store, new Integer(intIndex));
0877: return intIndex;
0878: }
0879: }
0880:
0881: /**
0882: * Adds a relation to a relation-valued attribute.
0883: *
0884: * @param value The value to add
0885: * @return the index assigned to the value, or -1 if the attribute is not
0886: * of type Attribute.RELATIONAL
0887: */
0888: public int addRelation(Instances value) {
0889:
0890: if (!isRelationValued()) {
0891: return -1;
0892: }
0893: if (!m_Header.equalHeaders(value)) {
0894: throw new IllegalArgumentException(
0895: "Incompatible value for "
0896: + "relation-valued attribute.");
0897: }
0898: Integer index = (Integer) m_Hashtable.get(value);
0899: if (index != null) {
0900: return index.intValue();
0901: } else {
0902: int intIndex = m_Values.size();
0903: m_Values.addElement(value);
0904: m_Hashtable.put(value, new Integer(intIndex));
0905: return intIndex;
0906: }
0907: }
0908:
0909: /**
0910: * Adds an attribute value. Creates a fresh list of attribute
0911: * values before adding it.
0912: *
0913: * @param value the attribute value
0914: */
0915: final void addValue(String value) {
0916:
0917: m_Values = (FastVector) m_Values.copy();
0918: m_Hashtable = (Hashtable) m_Hashtable.clone();
0919: forceAddValue(value);
0920: }
0921:
0922: /**
0923: * Produces a shallow copy of this attribute with a new name.
0924: *
0925: * @param newName the name of the new attribute
0926: * @return a copy of this attribute with the same index
0927: */
0928: //@ requires newName != null;
0929: //@ ensures \result.m_Name == newName;
0930: //@ ensures \result.m_Index == m_Index;
0931: //@ ensures \result.m_Type == m_Type;
0932: public final/*@ pure non_null @*/Attribute copy(String newName) {
0933:
0934: Attribute copy = new Attribute(newName);
0935:
0936: copy.m_Index = m_Index;
0937: copy.m_DateFormat = m_DateFormat;
0938: copy.m_Type = m_Type;
0939: copy.m_Values = m_Values;
0940: copy.m_Hashtable = m_Hashtable;
0941: copy.m_Header = m_Header;
0942: copy.setMetadata(m_Metadata);
0943:
0944: return copy;
0945: }
0946:
0947: /**
0948: * Removes a value of a nominal, string, or relation-valued
0949: * attribute. Creates a fresh list of attribute values before
0950: * removing it.
0951: *
0952: * @param index the value's index
0953: * @throws IllegalArgumentException if the attribute is not
0954: * of the correct type
0955: */
0956: //@ requires isNominal() || isString() || isRelationValued();
0957: //@ requires 0 <= index && index < m_Values.size();
0958: final void delete(int index) {
0959:
0960: if (!isNominal() && !isString() && !isRelationValued())
0961: throw new IllegalArgumentException(
0962: "Can only remove value of "
0963: + "nominal, string or relation-"
0964: + " valued attribute!");
0965: else {
0966: m_Values = (FastVector) m_Values.copy();
0967: m_Values.removeElementAt(index);
0968: if (!isRelationValued()) {
0969: Hashtable hash = new Hashtable(m_Hashtable.size());
0970: Enumeration enu = m_Hashtable.keys();
0971: while (enu.hasMoreElements()) {
0972: Object string = enu.nextElement();
0973: Integer valIndexObject = (Integer) m_Hashtable
0974: .get(string);
0975: int valIndex = valIndexObject.intValue();
0976: if (valIndex > index) {
0977: hash.put(string, new Integer(valIndex - 1));
0978: } else if (valIndex < index) {
0979: hash.put(string, valIndexObject);
0980: }
0981: }
0982: m_Hashtable = hash;
0983: }
0984: }
0985: }
0986:
0987: /**
0988: * Adds an attribute value.
0989: *
0990: * @param value the attribute value
0991: */
0992: //@ requires value != null;
0993: //@ ensures m_Values.size() == \old(m_Values.size()) + 1;
0994: final void forceAddValue(String value) {
0995:
0996: Object store = value;
0997: if (value.length() > STRING_COMPRESS_THRESHOLD) {
0998: try {
0999: store = new SerializedObject(value, true);
1000: } catch (Exception ex) {
1001: System.err
1002: .println("Couldn't compress string attribute value -"
1003: + " storing uncompressed.");
1004: }
1005: }
1006: m_Values.addElement(store);
1007: m_Hashtable.put(store, new Integer(m_Values.size() - 1));
1008: }
1009:
1010: /**
1011: * Sets the index of this attribute.
1012: *
1013: * @param index the index of this attribute
1014: */
1015: //@ requires 0 <= index;
1016: //@ assignable m_Index;
1017: //@ ensures m_Index == index;
1018: final void setIndex(int index) {
1019:
1020: m_Index = index;
1021: }
1022:
1023: /**
1024: * Sets a value of a nominal attribute or string attribute.
1025: * Creates a fresh list of attribute values before it is set.
1026: *
1027: * @param index the value's index
1028: * @param string the value
1029: * @throws IllegalArgumentException if the attribute is not nominal or
1030: * string.
1031: */
1032: //@ requires string != null;
1033: //@ requires isNominal() || isString();
1034: //@ requires 0 <= index && index < m_Values.size();
1035: final void setValue(int index, String string) {
1036:
1037: switch (m_Type) {
1038: case NOMINAL:
1039: case STRING:
1040: m_Values = (FastVector) m_Values.copy();
1041: m_Hashtable = (Hashtable) m_Hashtable.clone();
1042: Object store = string;
1043: if (string.length() > STRING_COMPRESS_THRESHOLD) {
1044: try {
1045: store = new SerializedObject(string, true);
1046: } catch (Exception ex) {
1047: System.err
1048: .println("Couldn't compress string attribute value -"
1049: + " storing uncompressed.");
1050: }
1051: }
1052: m_Hashtable.remove(m_Values.elementAt(index));
1053: m_Values.setElementAt(store, index);
1054: m_Hashtable.put(store, new Integer(index));
1055: break;
1056: default:
1057: throw new IllegalArgumentException(
1058: "Can only set values for nominal"
1059: + " or string attributes!");
1060: }
1061: }
1062:
1063: /**
1064: * Sets a value of a relation-valued attribute.
1065: * Creates a fresh list of attribute values before it is set.
1066: *
1067: * @param index the value's index
1068: * @param data the value
1069: * @throws IllegalArgumentException if the attribute is not
1070: * relation-valued.
1071: */
1072: final void setValue(int index, Instances data) {
1073:
1074: if (isRelationValued()) {
1075: if (!data.equalHeaders(m_Header)) {
1076: throw new IllegalArgumentException(
1077: "Can't set relational value. "
1078: + "Headers not compatible.");
1079: }
1080: m_Values = (FastVector) m_Values.copy();
1081: m_Values.setElementAt(data, index);
1082: } else {
1083: throw new IllegalArgumentException("Can only set value for"
1084: + " relation-valued attributes!");
1085: }
1086: }
1087:
1088: /**
1089: * Returns the given amount of milliseconds formatted according to the
1090: * current Date format.
1091: *
1092: * @param date the date, represented in milliseconds since
1093: * January 1, 1970, 00:00:00 GMT, to return as string
1094: * @return the formatted date
1095: */
1096: //@ requires isDate();
1097: public/*@pure@*/String formatDate(double date) {
1098: switch (m_Type) {
1099: case DATE:
1100: return m_DateFormat.format(new Date((long) date));
1101: default:
1102: throw new IllegalArgumentException(
1103: "Can only format date values for date"
1104: + " attributes!");
1105: }
1106: }
1107:
1108: /**
1109: * Parses the given String as Date, according to the current format and
1110: * returns the corresponding amount of milliseconds.
1111: *
1112: * @param string the date to parse
1113: * @return the date in milliseconds since January 1, 1970, 00:00:00 GMT
1114: * @throws ParseException if parsing fails
1115: */
1116: //@ requires isDate();
1117: //@ requires string != null;
1118: public double parseDate(String string) throws ParseException {
1119: switch (m_Type) {
1120: case DATE:
1121: long time = m_DateFormat.parse(string).getTime();
1122: // TODO put in a safety check here if we can't store the value in a double.
1123: return (double) time;
1124: default:
1125: throw new IllegalArgumentException(
1126: "Can only parse date values for date"
1127: + " attributes!");
1128: }
1129: }
1130:
1131: /**
1132: * Returns the properties supplied for this attribute.
1133: *
1134: * @return metadata for this attribute
1135: */
1136: public final/*@ pure @*/ProtectedProperties getMetadata() {
1137:
1138: return m_Metadata;
1139: }
1140:
1141: /**
1142: * Returns the ordering of the attribute. One of the following:
1143: *
1144: * ORDERING_SYMBOLIC - attribute values should be treated as symbols.
1145: * ORDERING_ORDERED - attribute values have a global ordering.
1146: * ORDERING_MODULO - attribute values have an ordering which wraps.
1147: *
1148: * @return the ordering type of the attribute
1149: */
1150: public final/*@ pure @*/int ordering() {
1151:
1152: return m_Ordering;
1153: }
1154:
1155: /**
1156: * Returns whether the attribute values are equally spaced.
1157: *
1158: * @return whether the attribute is regular or not
1159: */
1160: public final/*@ pure @*/boolean isRegular() {
1161:
1162: return m_IsRegular;
1163: }
1164:
1165: /**
1166: * Returns whether the attribute can be averaged meaningfully.
1167: *
1168: * @return whether the attribute can be averaged or not
1169: */
1170: public final/*@ pure @*/boolean isAveragable() {
1171:
1172: return m_IsAveragable;
1173: }
1174:
1175: /**
1176: * Returns whether the attribute has a zeropoint and may be
1177: * added meaningfully.
1178: *
1179: * @return whether the attribute has a zeropoint or not
1180: */
1181: public final/*@ pure @*/boolean hasZeropoint() {
1182:
1183: return m_HasZeropoint;
1184: }
1185:
1186: /**
1187: * Returns the attribute's weight.
1188: *
1189: * @return the attribute's weight as a double
1190: */
1191: public final/*@ pure @*/double weight() {
1192:
1193: return m_Weight;
1194: }
1195:
1196: /**
1197: * Sets the new attribute's weight
1198: *
1199: * @param value the new weight
1200: */
1201: public void setWeight(double value) {
1202: Properties props;
1203: Enumeration names;
1204: String name;
1205:
1206: m_Weight = value;
1207:
1208: // generate new metadata object
1209: props = new Properties();
1210: names = m_Metadata.propertyNames();
1211: while (names.hasMoreElements()) {
1212: name = (String) names.nextElement();
1213: if (!name.equals("weight"))
1214: props.setProperty(name, m_Metadata.getProperty(name));
1215: }
1216: props.setProperty("weight", "" + m_Weight);
1217: m_Metadata = new ProtectedProperties(props);
1218: }
1219:
1220: /**
1221: * Returns the lower bound of a numeric attribute.
1222: *
1223: * @return the lower bound of the specified numeric range
1224: */
1225: public final/*@ pure @*/double getLowerNumericBound() {
1226:
1227: return m_LowerBound;
1228: }
1229:
1230: /**
1231: * Returns whether the lower numeric bound of the attribute is open.
1232: *
1233: * @return whether the lower numeric bound is open or not (closed)
1234: */
1235: public final/*@ pure @*/boolean lowerNumericBoundIsOpen() {
1236:
1237: return m_LowerBoundIsOpen;
1238: }
1239:
1240: /**
1241: * Returns the upper bound of a numeric attribute.
1242: *
1243: * @return the upper bound of the specified numeric range
1244: */
1245: public final/*@ pure @*/double getUpperNumericBound() {
1246:
1247: return m_UpperBound;
1248: }
1249:
1250: /**
1251: * Returns whether the upper numeric bound of the attribute is open.
1252: *
1253: * @return whether the upper numeric bound is open or not (closed)
1254: */
1255: public final/*@ pure @*/boolean upperNumericBoundIsOpen() {
1256:
1257: return m_UpperBoundIsOpen;
1258: }
1259:
1260: /**
1261: * Determines whether a value lies within the bounds of the attribute.
1262: *
1263: * @param value the value to check
1264: * @return whether the value is in range
1265: */
1266: public final/*@ pure @*/boolean isInRange(double value) {
1267:
1268: // dates and missing values are a special case
1269: if (m_Type == DATE || value == Instance.missingValue())
1270: return true;
1271: if (m_Type != NUMERIC) {
1272: // do label range check
1273: int intVal = (int) value;
1274: if (intVal < 0 || intVal >= m_Hashtable.size())
1275: return false;
1276: } else {
1277: // do numeric bounds check
1278: if (m_LowerBoundIsOpen) {
1279: if (value <= m_LowerBound)
1280: return false;
1281: } else {
1282: if (value < m_LowerBound)
1283: return false;
1284: }
1285: if (m_UpperBoundIsOpen) {
1286: if (value >= m_UpperBound)
1287: return false;
1288: } else {
1289: if (value > m_UpperBound)
1290: return false;
1291: }
1292: }
1293: return true;
1294: }
1295:
1296: /**
1297: * Sets the metadata for the attribute. Processes the strings stored in the
1298: * metadata of the attribute so that the properties can be set up for the
1299: * easy-access metadata methods. Any strings sought that are omitted will
1300: * cause default values to be set.
1301: *
1302: * The following properties are recognised:
1303: * ordering, averageable, zeropoint, regular, weight, and range.
1304: *
1305: * All other properties can be queried and handled appropriately by classes
1306: * calling the getMetadata() method.
1307: *
1308: * @param metadata the metadata
1309: * @throws IllegalArgumentException if the properties are not consistent
1310: */
1311: //@ requires metadata != null;
1312: private void setMetadata(ProtectedProperties metadata) {
1313:
1314: m_Metadata = metadata;
1315:
1316: if (m_Type == DATE) {
1317: m_Ordering = ORDERING_ORDERED;
1318: m_IsRegular = true;
1319: m_IsAveragable = false;
1320: m_HasZeropoint = false;
1321: } else {
1322:
1323: // get ordering
1324: String orderString = m_Metadata.getProperty("ordering", "");
1325:
1326: // numeric ordered attributes are averagable and zeropoint by default
1327: String def;
1328: if (m_Type == NUMERIC
1329: && orderString.compareTo("modulo") != 0
1330: && orderString.compareTo("symbolic") != 0)
1331: def = "true";
1332: else
1333: def = "false";
1334:
1335: // determine boolean states
1336: m_IsAveragable = (m_Metadata
1337: .getProperty("averageable", def).compareTo("true") == 0);
1338: m_HasZeropoint = (m_Metadata.getProperty("zeropoint", def)
1339: .compareTo("true") == 0);
1340: // averagable or zeropoint implies regular
1341: if (m_IsAveragable || m_HasZeropoint)
1342: def = "true";
1343: m_IsRegular = (m_Metadata.getProperty("regular", def)
1344: .compareTo("true") == 0);
1345:
1346: // determine ordering
1347: if (orderString.compareTo("symbolic") == 0)
1348: m_Ordering = ORDERING_SYMBOLIC;
1349: else if (orderString.compareTo("ordered") == 0)
1350: m_Ordering = ORDERING_ORDERED;
1351: else if (orderString.compareTo("modulo") == 0)
1352: m_Ordering = ORDERING_MODULO;
1353: else {
1354: if (m_Type == NUMERIC || m_IsAveragable
1355: || m_HasZeropoint)
1356: m_Ordering = ORDERING_ORDERED;
1357: else
1358: m_Ordering = ORDERING_SYMBOLIC;
1359: }
1360: }
1361:
1362: // consistency checks
1363: if (m_IsAveragable && !m_IsRegular)
1364: throw new IllegalArgumentException(
1365: "An averagable attribute must be" + " regular");
1366: if (m_HasZeropoint && !m_IsRegular)
1367: throw new IllegalArgumentException(
1368: "A zeropoint attribute must be" + " regular");
1369: if (m_IsRegular && m_Ordering == ORDERING_SYMBOLIC)
1370: throw new IllegalArgumentException(
1371: "A symbolic attribute cannot be" + " regular");
1372: if (m_IsAveragable && m_Ordering != ORDERING_ORDERED)
1373: throw new IllegalArgumentException(
1374: "An averagable attribute must be" + " ordered");
1375: if (m_HasZeropoint && m_Ordering != ORDERING_ORDERED)
1376: throw new IllegalArgumentException(
1377: "A zeropoint attribute must be" + " ordered");
1378:
1379: // determine weight
1380: m_Weight = 1.0;
1381: String weightString = m_Metadata.getProperty("weight");
1382: if (weightString != null) {
1383: try {
1384: m_Weight = Double.valueOf(weightString).doubleValue();
1385: } catch (NumberFormatException e) {
1386: // Check if value is really a number
1387: throw new IllegalArgumentException(
1388: "Not a valid attribute weight: '"
1389: + weightString + "'");
1390: }
1391: }
1392:
1393: // determine numeric range
1394: if (m_Type == NUMERIC)
1395: setNumericRange(m_Metadata.getProperty("range"));
1396: }
1397:
1398: /**
1399: * Sets the numeric range based on a string. If the string is null the range
1400: * will default to [-inf,+inf]. A square brace represents a closed interval, a
1401: * curved brace represents an open interval, and 'inf' represents infinity.
1402: * Examples of valid range strings: "[-inf,20)","(-13.5,-5.2)","(5,inf]"
1403: *
1404: * @param rangeString the string to parse as the attribute's numeric range
1405: * @throws IllegalArgumentException if the range is not valid
1406: */
1407: //@ requires rangeString != null;
1408: private void setNumericRange(String rangeString) {
1409: // set defaults
1410: m_LowerBound = Double.NEGATIVE_INFINITY;
1411: m_LowerBoundIsOpen = false;
1412: m_UpperBound = Double.POSITIVE_INFINITY;
1413: m_UpperBoundIsOpen = false;
1414:
1415: if (rangeString == null)
1416: return;
1417:
1418: // set up a tokenzier to parse the string
1419: StreamTokenizer tokenizer = new StreamTokenizer(
1420: new StringReader(rangeString));
1421: tokenizer.resetSyntax();
1422: tokenizer.whitespaceChars(0, ' ');
1423: tokenizer.wordChars(' ' + 1, '\u00FF');
1424: tokenizer.ordinaryChar('[');
1425: tokenizer.ordinaryChar('(');
1426: tokenizer.ordinaryChar(',');
1427: tokenizer.ordinaryChar(']');
1428: tokenizer.ordinaryChar(')');
1429:
1430: try {
1431:
1432: // get opening brace
1433: tokenizer.nextToken();
1434:
1435: if (tokenizer.ttype == '[')
1436: m_LowerBoundIsOpen = false;
1437: else if (tokenizer.ttype == '(')
1438: m_LowerBoundIsOpen = true;
1439: else
1440: throw new IllegalArgumentException(
1441: "Expected opening brace on range," + " found: "
1442: + tokenizer.toString());
1443:
1444: // get lower bound
1445: tokenizer.nextToken();
1446: if (tokenizer.ttype != tokenizer.TT_WORD)
1447: throw new IllegalArgumentException(
1448: "Expected lower bound in range," + " found: "
1449: + tokenizer.toString());
1450: if (tokenizer.sval.compareToIgnoreCase("-inf") == 0)
1451: m_LowerBound = Double.NEGATIVE_INFINITY;
1452: else if (tokenizer.sval.compareToIgnoreCase("+inf") == 0)
1453: m_LowerBound = Double.POSITIVE_INFINITY;
1454: else if (tokenizer.sval.compareToIgnoreCase("inf") == 0)
1455: m_LowerBound = Double.NEGATIVE_INFINITY;
1456: else
1457: try {
1458: m_LowerBound = Double.valueOf(tokenizer.sval)
1459: .doubleValue();
1460: } catch (NumberFormatException e) {
1461: throw new IllegalArgumentException(
1462: "Expected lower bound in range,"
1463: + " found: '" + tokenizer.sval
1464: + "'");
1465: }
1466:
1467: // get separating comma
1468: if (tokenizer.nextToken() != ',')
1469: throw new IllegalArgumentException(
1470: "Expected comma in range," + " found: "
1471: + tokenizer.toString());
1472:
1473: // get upper bound
1474: tokenizer.nextToken();
1475: if (tokenizer.ttype != tokenizer.TT_WORD)
1476: throw new IllegalArgumentException(
1477: "Expected upper bound in range," + " found: "
1478: + tokenizer.toString());
1479: if (tokenizer.sval.compareToIgnoreCase("-inf") == 0)
1480: m_UpperBound = Double.NEGATIVE_INFINITY;
1481: else if (tokenizer.sval.compareToIgnoreCase("+inf") == 0)
1482: m_UpperBound = Double.POSITIVE_INFINITY;
1483: else if (tokenizer.sval.compareToIgnoreCase("inf") == 0)
1484: m_UpperBound = Double.POSITIVE_INFINITY;
1485: else
1486: try {
1487: m_UpperBound = Double.valueOf(tokenizer.sval)
1488: .doubleValue();
1489: } catch (NumberFormatException e) {
1490: throw new IllegalArgumentException(
1491: "Expected upper bound in range,"
1492: + " found: '" + tokenizer.sval
1493: + "'");
1494: }
1495:
1496: // get closing brace
1497: tokenizer.nextToken();
1498:
1499: if (tokenizer.ttype == ']')
1500: m_UpperBoundIsOpen = false;
1501: else if (tokenizer.ttype == ')')
1502: m_UpperBoundIsOpen = true;
1503: else
1504: throw new IllegalArgumentException(
1505: "Expected closing brace on range," + " found: "
1506: + tokenizer.toString());
1507:
1508: // check for rubbish on end
1509: if (tokenizer.nextToken() != tokenizer.TT_EOF)
1510: throw new IllegalArgumentException(
1511: "Expected end of range string," + " found: "
1512: + tokenizer.toString());
1513:
1514: } catch (IOException e) {
1515: throw new IllegalArgumentException(
1516: "IOException reading attribute range" + " string: "
1517: + e.getMessage());
1518: }
1519:
1520: if (m_UpperBound < m_LowerBound)
1521: throw new IllegalArgumentException("Upper bound ("
1522: + m_UpperBound + ") on numeric range is"
1523: + " less than lower bound (" + m_LowerBound + ")!");
1524: }
1525:
1526: /**
1527: * Simple main method for testing this class.
1528: *
1529: * @param ops the commandline options
1530: */
1531: //@ requires ops != null;
1532: //@ requires \nonnullelements(ops);
1533: public static void main(String[] ops) {
1534:
1535: try {
1536:
1537: // Create numeric attributes "length" and "weight"
1538: Attribute length = new Attribute("length");
1539: Attribute weight = new Attribute("weight");
1540:
1541: // Create date attribute "date"
1542: Attribute date = new Attribute("date",
1543: "yyyy-MM-dd HH:mm:ss");
1544:
1545: System.out.println(date);
1546: double dd = date.parseDate("2001-04-04 14:13:55");
1547: System.out.println("Test date = " + dd);
1548: System.out.println(date.formatDate(dd));
1549:
1550: dd = new Date().getTime();
1551: System.out.println("Date now = " + dd);
1552: System.out.println(date.formatDate(dd));
1553:
1554: // Create vector to hold nominal values "first", "second", "third"
1555: FastVector my_nominal_values = new FastVector(3);
1556: my_nominal_values.addElement("first");
1557: my_nominal_values.addElement("second");
1558: my_nominal_values.addElement("third");
1559:
1560: // Create nominal attribute "position"
1561: Attribute position = new Attribute("position",
1562: my_nominal_values);
1563:
1564: // Print the name of "position"
1565: System.out.println("Name of \"position\": "
1566: + position.name());
1567:
1568: // Print the values of "position"
1569: Enumeration attValues = position.enumerateValues();
1570: while (attValues.hasMoreElements()) {
1571: String string = (String) attValues.nextElement();
1572: System.out.println("Value of \"position\": " + string);
1573: }
1574:
1575: // Shallow copy attribute "position"
1576: Attribute copy = (Attribute) position.copy();
1577:
1578: // Test if attributes are the same
1579: System.out.println("Copy is the same as original: "
1580: + copy.equals(position));
1581:
1582: // Print index of attribute "weight" (should be unset: -1)
1583: System.out
1584: .println("Index of attribute \"weight\" (should be -1): "
1585: + weight.index());
1586:
1587: // Print index of value "first" of attribute "position"
1588: System.out
1589: .println("Index of value \"first\" of \"position\" (should be 0): "
1590: + position.indexOfValue("first"));
1591:
1592: // Tests type of attribute "position"
1593: System.out.println("\"position\" is numeric: "
1594: + position.isNumeric());
1595: System.out.println("\"position\" is nominal: "
1596: + position.isNominal());
1597: System.out.println("\"position\" is string: "
1598: + position.isString());
1599:
1600: // Prints name of attribute "position"
1601: System.out.println("Name of \"position\": "
1602: + position.name());
1603:
1604: // Prints number of values of attribute "position"
1605: System.out.println("Number of values for \"position\": "
1606: + position.numValues());
1607:
1608: // Prints the values (againg)
1609: for (int i = 0; i < position.numValues(); i++) {
1610: System.out.println("Value " + i + ": "
1611: + position.value(i));
1612: }
1613:
1614: // Prints the attribute "position" in ARFF format
1615: System.out.println(position);
1616:
1617: // Checks type of attribute "position" using constants
1618: switch (position.type()) {
1619: case Attribute.NUMERIC:
1620: System.out.println("\"position\" is numeric");
1621: break;
1622: case Attribute.NOMINAL:
1623: System.out.println("\"position\" is nominal");
1624: break;
1625: case Attribute.STRING:
1626: System.out.println("\"position\" is string");
1627: break;
1628: case Attribute.DATE:
1629: System.out.println("\"position\" is date");
1630: break;
1631: case Attribute.RELATIONAL:
1632: System.out.println("\"position\" is relation-valued");
1633: break;
1634: default:
1635: System.out.println("\"position\" has unknown type");
1636: }
1637:
1638: FastVector atts = new FastVector(1);
1639: atts.addElement(position);
1640: Instances relation = new Instances("Test", atts, 0);
1641: Attribute relationValuedAtt = new Attribute("test",
1642: relation);
1643: System.out.println(relationValuedAtt);
1644: } catch (Exception e) {
1645: e.printStackTrace();
1646: }
1647: }
1648: }
|