source: src/main/java/weka/core/Capabilities.java @ 24

Last change on this file since 24 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 46.4 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 * Capabilities.java
19 * Copyright (C) 2006 University of Waikato, Hamilton, New Zealand
20 */
21
22package weka.core;
23
24import weka.core.converters.ConverterUtils.DataSource;
25
26import java.io.Serializable;
27import java.util.Collections;
28import java.util.HashSet;
29import java.util.Iterator;
30import java.util.Properties;
31import java.util.Vector;
32
33/**
34 * A class that describes the capabilites (e.g., handling certain types of
35 * attributes, missing values, types of classes, etc.) of a specific
36 * classifier. By default, the classifier is capable of nothing. This
37 * ensures that new features have to be enabled explicitly. <p/>
38 *
39 * A common code fragment for making use of the capabilities in a classifier
40 * would be this:
41 * <pre>
42 * public void <b>buildClassifier</b>(Instances instances) throws Exception {
43 *   // can the classifier handle the data?
44 *   getCapabilities().<b>testWithFail(instances)</b>;
45 *   ...
46 *   // possible deletion of instances with missing class labels, etc.
47 * </pre>
48 * For only testing a single attribute, use this:
49 * <pre>
50 *   ...
51 *   Attribute att = instances.attribute(0);
52 *   getCapabilities().<b>testWithFail(att)</b>;
53 *   ...
54 * </pre>
55 * Or for testing the class attribute (uses the capabilities that are
56 * especially for the class):
57 * <pre>
58 *   ...
59 *   Attribute att = instances.classAttribute();
60 *   getCapabilities().<b>testWithFail(att, <i>true</i>)</b>;
61 *   ...
62 * </pre>
63 *
64 * @author  FracPete (fracpete at waikato dot ac dot nz)
65 * @version $Revision: 5953 $
66 */
67public class Capabilities 
68  implements Cloneable, Serializable, RevisionHandler {
69 
70  /** serialversion UID */
71  static final long serialVersionUID = -5478590032325567849L; 
72
73  /** the properties file for managing the tests */
74  public final static String PROPERTIES_FILE = "weka/core/Capabilities.props";
75
76  /** the actual properties */
77  protected static Properties PROPERTIES;
78 
79  /** defines an attribute type */
80  private final static int ATTRIBUTE = 1;
81 
82  /** defines a class type */
83  private final static int CLASS = 2;
84 
85  /** defines an attribute capability */
86  private final static int ATTRIBUTE_CAPABILITY = 4;
87 
88  /** defines a class capability */
89  private final static int CLASS_CAPABILITY = 8;
90 
91  /** defines a other capability */
92  private final static int OTHER_CAPABILITY = 16;
93
94  /** enumeration of all capabilities */
95  public enum Capability {
96    // attributes
97    /** can handle nominal attributes */
98    NOMINAL_ATTRIBUTES(ATTRIBUTE + ATTRIBUTE_CAPABILITY, "Nominal attributes"),
99    /** can handle binary attributes */
100    BINARY_ATTRIBUTES(ATTRIBUTE + ATTRIBUTE_CAPABILITY, "Binary attributes"),
101    /** can handle unary attributes */
102    UNARY_ATTRIBUTES(ATTRIBUTE + ATTRIBUTE_CAPABILITY, "Unary attributes"),
103    /** can handle empty nominal attributes */
104    EMPTY_NOMINAL_ATTRIBUTES(ATTRIBUTE + ATTRIBUTE_CAPABILITY, "Empty nominal attributes"),
105    /** can handle numeric attributes */
106    NUMERIC_ATTRIBUTES(ATTRIBUTE + ATTRIBUTE_CAPABILITY, "Numeric attributes"),
107    /** can handle date attributes */
108    DATE_ATTRIBUTES(ATTRIBUTE + ATTRIBUTE_CAPABILITY, "Date attributes"),
109    /** can handle string attributes */
110    STRING_ATTRIBUTES(ATTRIBUTE + ATTRIBUTE_CAPABILITY, "String attributes"),
111    /** can handle relational attributes */
112    RELATIONAL_ATTRIBUTES(ATTRIBUTE + ATTRIBUTE_CAPABILITY, "Relational attributes"),
113    /** can handle missing values in attributes */
114    MISSING_VALUES(ATTRIBUTE_CAPABILITY, "Missing values"),
115    // class
116    /** can handle data without class attribute, eg clusterers */
117    NO_CLASS(CLASS_CAPABILITY, "No class"),
118    /** can handle nominal classes */
119    NOMINAL_CLASS(CLASS + CLASS_CAPABILITY, "Nominal class"),
120    /** can handle binary classes */
121    BINARY_CLASS(CLASS + CLASS_CAPABILITY, "Binary class"),
122    /** can handle unary classes */
123    UNARY_CLASS(CLASS + CLASS_CAPABILITY, "Unary class"),
124    /** can handle empty nominal classes */
125    EMPTY_NOMINAL_CLASS(CLASS + CLASS_CAPABILITY, "Empty nominal class"),
126    /** can handle numeric classes */
127    NUMERIC_CLASS(CLASS + CLASS_CAPABILITY, "Numeric class"),
128    /** can handle date classes */
129    DATE_CLASS(CLASS + CLASS_CAPABILITY, "Date class"),
130    /** can handle string classes */
131    STRING_CLASS(CLASS + CLASS_CAPABILITY, "String class"),
132    /** can handle relational classes */
133    RELATIONAL_CLASS(CLASS + CLASS_CAPABILITY, "Relational class"),
134    /** can handle missing values in class attribute */
135    MISSING_CLASS_VALUES(CLASS_CAPABILITY, "Missing class values"),
136    // other
137    /** can handle multi-instance data */
138    ONLY_MULTIINSTANCE(OTHER_CAPABILITY, "Only multi-Instance data");
139
140    /** the flags for the capabilities */
141    private int m_Flags = 0;
142   
143    /** the display string */
144    private String m_Display;
145   
146    /**
147     * initializes the capability with the given flags
148     *
149     * @param flags     "meta-data" for the capability
150     * @param display   the display string (must be unique!)
151     */
152    private Capability(int flags, String display) {
153      m_Flags   = flags;
154      m_Display = display;
155    }
156   
157    /**
158     * returns true if the capability is an attribute
159     *
160     * @return true if the capability is an attribute
161     */
162    public boolean isAttribute() {
163      return ((m_Flags & ATTRIBUTE) == ATTRIBUTE);
164    }
165   
166    /**
167     * returns true if the capability is a class
168     *
169     * @return true if the capability is a class
170     */
171    public boolean isClass() {
172      return ((m_Flags & CLASS) == CLASS);
173    }
174   
175    /**
176     * returns true if the capability is an attribute capability
177     *
178     * @return true if the capability is an attribute capability
179     */
180    public boolean isAttributeCapability() {
181      return ((m_Flags & ATTRIBUTE_CAPABILITY) == ATTRIBUTE_CAPABILITY);
182    }
183   
184    /**
185     * returns true if the capability is a class capability
186     *
187     * @return true if the capability is a class capability
188     */
189    public boolean isOtherCapability() {
190      return ((m_Flags & OTHER_CAPABILITY) == OTHER_CAPABILITY);
191    }
192   
193    /**
194     * returns true if the capability is a other capability
195     *
196     * @return true if the capability is a other capability
197     */
198    public boolean isClassCapability() {
199      return ((m_Flags & CLASS_CAPABILITY) == CLASS_CAPABILITY);
200    }
201   
202    /**
203     * returns the display string of the capability
204     *
205     * @return the display string
206     */
207    public String toString() {
208      return m_Display;
209    }
210  };
211
212  /** the object that owns this capabilities instance */
213  protected CapabilitiesHandler m_Owner;
214 
215  /** the hashset for storing the active capabilities */
216  protected HashSet<Capability> m_Capabilities;
217 
218  /** the hashset for storing dependent capabilities, eg for meta-classifiers */
219  protected HashSet<Capability> m_Dependencies;
220 
221  /** the reason why the test failed, used to throw an exception */
222  protected Exception m_FailReason = null;
223
224  /** the minimum number of instances in a dataset */
225  protected int m_MinimumNumberInstances = 1;
226
227  /** whether to perform any tests at all */
228  protected boolean m_Test;
229
230  /** whether to perform data based tests */
231  protected boolean m_InstancesTest;
232
233  /** whether to perform attribute based tests */
234  protected boolean m_AttributeTest;
235
236  /** whether to test for missing values */
237  protected boolean m_MissingValuesTest;
238
239  /** whether to test for missing class values */
240  protected boolean m_MissingClassValuesTest;
241
242  /** whether to test for minimum number of instances */
243  protected boolean m_MinimumNumberInstancesTest;
244 
245  /**
246   * initializes the capabilities for the given owner
247   *
248   * @param owner       the object that produced this Capabilities instance
249   */
250  public Capabilities(CapabilitiesHandler owner) {
251    super();
252
253    setOwner(owner);
254    m_Capabilities = new HashSet<Capability>();
255    m_Dependencies = new HashSet<Capability>();
256
257    // load properties
258    if (PROPERTIES == null) {
259      try {
260        PROPERTIES = Utils.readProperties(PROPERTIES_FILE);
261      }
262      catch (Exception e) {
263        e.printStackTrace();
264        PROPERTIES = new Properties();
265      }
266    }
267   
268    m_Test                       = Boolean.parseBoolean(PROPERTIES.getProperty("Test", "true"));
269    m_InstancesTest              = Boolean.parseBoolean(PROPERTIES.getProperty("InstancesTest", "true")) && m_Test;
270    m_AttributeTest              = Boolean.parseBoolean(PROPERTIES.getProperty("AttributeTest", "true")) && m_Test;
271    m_MissingValuesTest          = Boolean.parseBoolean(PROPERTIES.getProperty("MissingValuesTest", "true")) && m_Test;
272    m_MissingClassValuesTest     = Boolean.parseBoolean(PROPERTIES.getProperty("MissingClassValuesTest", "true")) && m_Test;
273    m_MinimumNumberInstancesTest = Boolean.parseBoolean(PROPERTIES.getProperty("MinimumNumberInstancesTest", "true")) && m_Test;
274  }
275 
276  /**
277   * Creates and returns a copy of this object.
278   *
279   * @return    a clone of this object
280   */
281  public Object clone() {
282    Capabilities    result;
283
284    result = new Capabilities(m_Owner);
285    result.assign(this);
286
287    return result;
288  }
289 
290  /**
291   * retrieves the data from the given Capabilities object
292   *
293   * @param c     the capabilities object to initialize with
294   */
295  public void assign(Capabilities c) {
296    for (Capability cap: Capability.values()) {
297      // capability
298      if (c.handles(cap))
299        enable(cap);
300      else
301        disable(cap);
302      // dependency
303      if (c.hasDependency(cap))
304        enableDependency(cap);
305      else
306        disableDependency(cap);
307    }
308
309    setMinimumNumberInstances(c.getMinimumNumberInstances());
310  }
311
312  /**
313   * performs an AND conjunction with the capabilities of the given
314   * Capabilities object and updates itself
315   *
316   * @param c     the capabilities to AND with
317   */
318  public void and(Capabilities c) {
319    for (Capability cap: Capability.values()) {
320      // capability
321      if (handles(cap) && c.handles(cap))
322        m_Capabilities.add(cap);
323      else
324        m_Capabilities.remove(cap);
325      // dependency
326      if (hasDependency(cap) && c.hasDependency(cap))
327        m_Dependencies.add(cap);
328      else
329        m_Dependencies.remove(cap);
330    }
331   
332    // minimum number of instances that both handlers need at least to work
333    if (c.getMinimumNumberInstances() > getMinimumNumberInstances())
334      setMinimumNumberInstances(c.getMinimumNumberInstances());
335  }
336
337  /**
338   * performs an OR conjunction with the capabilities of the given
339   * Capabilities object and updates itself
340   *
341   * @param c     the capabilities to OR with
342   */
343  public void or(Capabilities c) {
344    for (Capability cap: Capability.values()) {
345      // capability
346      if (handles(cap) || c.handles(cap))
347        m_Capabilities.add(cap);
348      else
349        m_Capabilities.remove(cap);
350      // dependency
351      if (hasDependency(cap) || c.hasDependency(cap))
352        m_Dependencies.add(cap);
353      else
354        m_Dependencies.remove(cap);
355    }
356   
357    if (c.getMinimumNumberInstances() < getMinimumNumberInstances())
358      setMinimumNumberInstances(c.getMinimumNumberInstances());
359  }
360 
361  /**
362   * Returns true if the currently set capabilities support at least all of
363   * the capabiliites of the given Capabilities object (checks only the enum!)
364   *
365   * @param c   the capabilities to support at least
366   * @return    true if all the requested capabilities are supported
367   */
368  public boolean supports(Capabilities c) {
369    boolean     result;
370   
371    result = true;
372   
373    for (Capability cap: Capability.values()) {
374      if (c.handles(cap) && !handles(cap)) {
375        result = false;
376        break;
377      }
378    }
379
380    return result;
381  }
382 
383  /**
384   * Returns true if the currently set capabilities support (or have a
385   * dependency) at least all of the capabilities of the given Capabilities
386   * object (checks only the enum!)
387   *
388   * @param c   the capabilities (or dependencies) to support at least
389   * @return    true if all the requested capabilities are supported (or at
390   *            least have a dependency)
391   */
392  public boolean supportsMaybe(Capabilities c) {
393    boolean     result;
394   
395    result = true;
396   
397    for (Capability cap: Capability.values()) {
398      if (c.handles(cap) && !(handles(cap) || hasDependency(cap))) {
399        result = false;
400        break;
401      }
402    }
403
404    return result;
405  }
406
407  /**
408   * sets the owner of this capabilities object
409   *
410   * @param value       the new owner
411   */
412  public void setOwner(CapabilitiesHandler value) {
413    m_Owner = value;
414  }
415 
416  /**
417   * returns the owner of this capabilities object
418   *
419   * @return            the current owner of this capabilites object
420   */
421  public CapabilitiesHandler getOwner() {
422    return m_Owner;
423  }
424
425  /**
426   * sets the minimum number of instances that have to be in the dataset
427   *
428   * @param value       the minimum number of instances
429   */
430  public void setMinimumNumberInstances(int value) {
431    if (value >= 0)
432      m_MinimumNumberInstances = value;
433  }
434 
435  /**
436   * returns the minimum number of instances that have to be in the dataset
437   *
438   * @return            the minimum number of instances
439   */
440  public int getMinimumNumberInstances() {
441    return m_MinimumNumberInstances;
442  }
443 
444  /**
445   * Returns an Iterator over the stored capabilities
446   *
447   * @return iterator over the current capabilities
448   */
449  public Iterator capabilities() {
450    return m_Capabilities.iterator();
451  }
452 
453  /**
454   * Returns an Iterator over the stored dependencies
455   *
456   * @return iterator over the current dependencies
457   */
458  public Iterator dependencies() {
459    return m_Dependencies.iterator();
460  }
461 
462  /**
463   * enables the given capability.
464   * Enabling NOMINAL_ATTRIBUTES also enables BINARY_ATTRIBUTES,
465   * UNARY_ATTRIBUTES and EMPTY_NOMINAL_ATTRIBUTES.
466   * Enabling BINARY_ATTRIBUTES also enables UNARY_ATTRIBUTES and
467   * EMPTY_NOMINAL_ATTRIBUTES.
468   * Enabling UNARY_ATTRIBUTES also enables EMPTY_NOMINAL_ATTRIBUTES.
469   * But NOMINAL_CLASS only enables BINARY_CLASS, since normal schemes in Weka
470   * don't work with datasets that have only 1 class label (or none).
471   *
472   * @param c     the capability to enable
473   */
474  public void enable(Capability c) {
475    // attributes
476    if (c == Capability.NOMINAL_ATTRIBUTES) {
477      enable(Capability.BINARY_ATTRIBUTES);
478    }
479    else if (c == Capability.BINARY_ATTRIBUTES) {
480      enable(Capability.UNARY_ATTRIBUTES);
481    }
482    else if (c == Capability.UNARY_ATTRIBUTES) {
483      enable(Capability.EMPTY_NOMINAL_ATTRIBUTES);
484    }
485    // class
486    else if (c == Capability.NOMINAL_CLASS) {
487      enable(Capability.BINARY_CLASS);
488    }
489
490    m_Capabilities.add(c);
491  }
492 
493  /**
494   * enables the dependency flag for the given capability
495   * Enabling NOMINAL_ATTRIBUTES also enables BINARY_ATTRIBUTES,
496   * UNARY_ATTRIBUTES and EMPTY_NOMINAL_ATTRIBUTES.
497   * Enabling BINARY_ATTRIBUTES also enables UNARY_ATTRIBUTES and
498   * EMPTY_NOMINAL_ATTRIBUTES.
499   * Enabling UNARY_ATTRIBUTES also enables EMPTY_NOMINAL_ATTRIBUTES.
500   * But NOMINAL_CLASS only enables BINARY_CLASS, since normal schemes in Weka
501   * don't work with datasets that have only 1 class label (or none).
502   *
503   * @param c     the capability to enable the dependency flag for
504   */
505  public void enableDependency(Capability c) {
506    // attributes
507    if (c == Capability.NOMINAL_ATTRIBUTES) {
508      enableDependency(Capability.BINARY_ATTRIBUTES);
509    }
510    else if (c == Capability.BINARY_ATTRIBUTES) {
511      enableDependency(Capability.UNARY_ATTRIBUTES);
512    }
513    else if (c == Capability.UNARY_ATTRIBUTES) {
514      enableDependency(Capability.EMPTY_NOMINAL_ATTRIBUTES);
515    }
516    // class
517    else if (c == Capability.NOMINAL_CLASS) {
518      enableDependency(Capability.BINARY_CLASS);
519    }
520
521    m_Dependencies.add(c);
522  }
523 
524  /**
525   * enables all class types
526   *
527   * @see #disableAllClasses()
528   * @see #getClassCapabilities()
529   */
530  public void enableAllClasses() {
531    for (Capability cap: Capability.values()) {
532      if (cap.isClass())
533        enable(cap);
534    }
535  }
536 
537  /**
538   * enables all class type dependencies
539   *
540   * @see #disableAllClassDependencies()
541   * @see #getClassCapabilities()
542   */
543  public void enableAllClassDependencies() {
544    for (Capability cap: Capability.values()) {
545      if (cap.isClass())
546        enableDependency(cap);
547    }
548  }
549 
550  /**
551   * enables all attribute types
552   *
553   * @see #disableAllAttributes()
554   * @see #getAttributeCapabilities()
555   */
556  public void enableAllAttributes() {
557    for (Capability cap: Capability.values()) {
558      if (cap.isAttribute())
559        enable(cap);
560    }
561  }
562 
563  /**
564   * enables all attribute type dependencies
565   *
566   * @see #disableAllAttributeDependencies()
567   * @see #getAttributeCapabilities()
568   */
569  public void enableAllAttributeDependencies() {
570    for (Capability cap: Capability.values()) {
571      if (cap.isAttribute())
572        enableDependency(cap);
573    }
574  }
575 
576  /**
577   * enables all attribute and class types (including dependencies)
578   */
579  public void enableAll() {
580    enableAllAttributes();
581    enableAllAttributeDependencies();
582    enableAllClasses();
583    enableAllClassDependencies();
584  }
585
586  /**
587   * disables the given capability
588   * Disabling NOMINAL_ATTRIBUTES also disables BINARY_ATTRIBUTES,
589   * UNARY_ATTRIBUTES and EMPTY_NOMINAL_ATTRIBUTES.
590   * Disabling BINARY_ATTRIBUTES also disables UNARY_ATTRIBUTES and
591   * EMPTY_NOMINAL_ATTRIBUTES.
592   * Disabling UNARY_ATTRIBUTES also disables EMPTY_NOMINAL_ATTRIBUTES.
593   * The same hierarchy applies to the class capabilities.
594   *
595   * @param c     the capability to disable
596   */
597  public void disable(Capability c) {
598    // attributes
599    if (c == Capability.NOMINAL_ATTRIBUTES) {
600      disable(Capability.BINARY_ATTRIBUTES);
601    }
602    else if (c == Capability.BINARY_ATTRIBUTES) {
603      disable(Capability.UNARY_ATTRIBUTES);
604    }
605    else if (c == Capability.UNARY_ATTRIBUTES) {
606      disable(Capability.EMPTY_NOMINAL_ATTRIBUTES);
607    }
608    // class
609    else if (c == Capability.NOMINAL_CLASS) {
610      disable(Capability.BINARY_CLASS);
611    }
612    else if (c == Capability.BINARY_CLASS) {
613      disable(Capability.UNARY_CLASS);
614    }
615    else if (c == Capability.UNARY_CLASS) {
616      disable(Capability.EMPTY_NOMINAL_CLASS);
617    }
618
619    m_Capabilities.remove(c);
620  }
621
622  /**
623   * disables the dependency of the given capability
624   * Disabling NOMINAL_ATTRIBUTES also disables BINARY_ATTRIBUTES,
625   * UNARY_ATTRIBUTES and EMPTY_NOMINAL_ATTRIBUTES.
626   * Disabling BINARY_ATTRIBUTES also disables UNARY_ATTRIBUTES and
627   * EMPTY_NOMINAL_ATTRIBUTES.
628   * Disabling UNARY_ATTRIBUTES also disables EMPTY_NOMINAL_ATTRIBUTES.
629   * The same hierarchy applies to the class capabilities.
630   *
631   * @param c     the capability to disable the dependency flag for
632   */
633  public void disableDependency(Capability c) {
634    // attributes
635    if (c == Capability.NOMINAL_ATTRIBUTES) {
636      disableDependency(Capability.BINARY_ATTRIBUTES);
637    }
638    else if (c == Capability.BINARY_ATTRIBUTES) {
639      disableDependency(Capability.UNARY_ATTRIBUTES);
640    }
641    else if (c == Capability.UNARY_ATTRIBUTES) {
642      disableDependency(Capability.EMPTY_NOMINAL_ATTRIBUTES);
643    }
644    // class
645    else if (c == Capability.NOMINAL_CLASS) {
646      disableDependency(Capability.BINARY_CLASS);
647    }
648    else if (c == Capability.BINARY_CLASS) {
649      disableDependency(Capability.UNARY_CLASS);
650    }
651    else if (c == Capability.UNARY_CLASS) {
652      disableDependency(Capability.EMPTY_NOMINAL_CLASS);
653    }
654
655    m_Dependencies.remove(c);
656  }
657 
658  /**
659   * disables all class types
660   *
661   * @see #enableAllClasses()
662   * @see #getClassCapabilities()
663   */
664  public void disableAllClasses() {
665    for (Capability cap: Capability.values()) {
666      if (cap.isClass())
667        disable(cap);
668    }
669  }
670 
671  /**
672   * disables all class type dependencies
673   *
674   * @see #enableAllClassDependencies()
675   * @see #getClassCapabilities()
676   */
677  public void disableAllClassDependencies() {
678    for (Capability cap: Capability.values()) {
679      if (cap.isClass())
680        disableDependency(cap);
681    }
682  }
683 
684  /**
685   * disables all attribute types
686   *
687   * @see #enableAllAttributes()
688   * @see #getAttributeCapabilities()
689   */
690  public void disableAllAttributes() {
691    for (Capability cap: Capability.values()) {
692      if (cap.isAttribute())
693        disable(cap);
694    }
695  }
696 
697  /**
698   * disables all attribute type dependencies
699   *
700   * @see #enableAllAttributeDependencies()
701   * @see #getAttributeCapabilities()
702   */
703  public void disableAllAttributeDependencies() {
704    for (Capability cap: Capability.values()) {
705      if (cap.isAttribute())
706        disableDependency(cap);
707    }
708  }
709 
710  /**
711   * disables all attribute and class types (including dependencies)
712   */
713  public void disableAll() {
714    disableAllAttributes();
715    disableAllAttributeDependencies();
716    disableAllClasses();
717    disableAllClassDependencies();
718  }
719 
720  /**
721   * returns all class capabilities
722   *
723   * @return            all capabilities regarding the class
724   * @see #enableAllClasses()
725   * @see #disableAllClasses()
726   */
727  public Capabilities getClassCapabilities() {
728    Capabilities        result;
729   
730    result = new Capabilities(getOwner());
731   
732    for (Capability cap: Capability.values()) {
733      if (cap.isClassCapability()) {
734        if (handles(cap))
735          result.m_Capabilities.add(cap);
736      }
737    }
738   
739    return result;
740  }
741 
742  /**
743   * returns all attribute capabilities
744   *
745   * @return            all capabilities regarding attributes
746   * @see #enableAllAttributes()
747   * @see #disableAllAttributes()
748   */
749  public Capabilities getAttributeCapabilities() {
750    Capabilities        result;
751   
752    result = new Capabilities(getOwner());
753   
754    for (Capability cap: Capability.values()) {
755      if (cap.isAttributeCapability()) {
756        if (handles(cap))
757          result.m_Capabilities.add(cap);
758      }
759    }
760   
761    return result;
762  }
763 
764  /**
765   * returns all other capabilities, besides class and attribute related ones
766   *
767   * @return            all other capabilities, besides class and attribute
768   *                    related ones
769   */
770  public Capabilities getOtherCapabilities() {
771    Capabilities        result;
772   
773    result = new Capabilities(getOwner());
774   
775    for (Capability cap: Capability.values()) {
776      if (cap.isOtherCapability()) {
777        if (handles(cap))
778          result.m_Capabilities.add(cap);
779      }
780    }
781   
782    return result;
783  }
784
785  /**
786   * returns true if the classifier handler has the specified capability
787   *
788   * @param c     the capability to test
789   * @return      true if the classifier handler has the capability
790   */
791  public boolean handles(Capability c) {
792    return m_Capabilities.contains(c);
793  }
794
795  /**
796   * returns true if the classifier handler has a dependency for the specified
797   * capability
798   *
799   * @param c     the capability to test
800   * @return      true if the classifier handler has a dependency for the
801   *               capability
802   */
803  public boolean hasDependency(Capability c) {
804    return m_Dependencies.contains(c);
805  }
806 
807  /**
808   * Checks whether there are any dependencies at all
809   *
810   * @return true if there is at least one dependency for a capability
811   */
812  public boolean hasDependencies() {
813    return (m_Dependencies.size() > 0);
814  }
815
816  /**
817   * returns the reason why the tests failed, is null if tests succeeded
818   *
819   * @return            the reason why the tests failed
820   */
821  public Exception getFailReason() {
822    return m_FailReason;
823  }
824 
825  /**
826   * Generates the message for, e.g., an exception. Adds the classname before the
827   * actual message and returns that string.
828   *
829   * @param msg         the actual content of the message, e.g., exception
830   * @return            the new message
831   */
832  protected String createMessage(String msg) {
833    String      result;
834   
835    result = "";
836   
837    if (getOwner() != null)
838      result = getOwner().getClass().getName();
839    else
840      result = "<anonymous>";
841     
842    result += ": " + msg;
843   
844    return result;
845  }
846 
847  /**
848   * Test the given attribute, whether it can be processed by the handler,
849   * given its capabilities. The method assumes that the specified attribute
850   * is not the class attribute.
851   *
852   * @param att         the attribute to test
853   * @return            true if all the tests succeeded
854   * @see               #test(Attribute, boolean)
855   */
856  public boolean test(Attribute att) {
857    return test(att, false);
858  }
859 
860  /**
861   * Test the given attribute, whether it can be processed by the handler,
862   * given its capabilities.
863   *
864   * @param att         the attribute to test
865   * @param isClass     whether this attribute is the class attribute
866   * @return            true if all the tests succeeded
867   * @see               #m_AttributeTest
868   */
869  public boolean test(Attribute att, boolean isClass) {
870    boolean             result;
871    Capability          cap;
872    Capability          capBinary;
873    Capability          capUnary;
874    Capability          capEmpty;
875    String              errorStr;
876   
877    result = true;
878   
879    // shall we test the data?
880    if (!m_AttributeTest)
881      return result;
882
883    // for exception
884    if (isClass)
885      errorStr  = "class";
886    else
887      errorStr  = "attributes";
888   
889    switch (att.type()) {
890      case Attribute.NOMINAL:
891        if (isClass) {
892          cap       = Capability.NOMINAL_CLASS;
893          capBinary = Capability.BINARY_CLASS;
894          capUnary  = Capability.UNARY_CLASS;
895          capEmpty  = Capability.EMPTY_NOMINAL_CLASS;
896        }
897        else {
898          cap       = Capability.NOMINAL_ATTRIBUTES;
899          capBinary = Capability.BINARY_ATTRIBUTES;
900          capUnary  = Capability.UNARY_ATTRIBUTES;
901          capEmpty  = Capability.EMPTY_NOMINAL_ATTRIBUTES;
902        }
903       
904        if (handles(cap) && (att.numValues() > 2))
905          break;
906        else if (handles(capBinary) && (att.numValues() == 2))
907          break;
908        else if (handles(capUnary) && (att.numValues() == 1))
909          break;
910        else if (handles(capEmpty) && (att.numValues() == 0))
911          break;
912
913        if (att.numValues() == 0) {
914          m_FailReason = new UnsupportedAttributeTypeException(
915              createMessage("Cannot handle empty nominal " + errorStr + "!"));
916          result = false;
917        }
918        if (att.numValues() == 1) {
919          m_FailReason = new UnsupportedAttributeTypeException(
920              createMessage("Cannot handle unary " + errorStr + "!"));
921          result = false;
922        }
923        else if (att.numValues() == 2) {
924          m_FailReason = new UnsupportedAttributeTypeException(
925              createMessage("Cannot handle binary " + errorStr + "!"));
926          result = false;
927        }
928        else {
929          m_FailReason = new UnsupportedAttributeTypeException(
930              createMessage("Cannot handle multi-valued nominal " + errorStr + "!"));
931          result = false;
932        }
933        break;
934
935      case Attribute.NUMERIC:
936        if (isClass)
937          cap = Capability.NUMERIC_CLASS;
938        else
939          cap = Capability.NUMERIC_ATTRIBUTES;
940       
941        if (!handles(cap)) {
942          m_FailReason = new UnsupportedAttributeTypeException(
943                              createMessage("Cannot handle numeric " + errorStr + "!"));
944          result = false;
945        }
946        break;
947
948      case Attribute.DATE:
949        if (isClass)
950          cap = Capability.DATE_CLASS;
951        else
952          cap = Capability.DATE_ATTRIBUTES;
953       
954        if (!handles(cap)) {
955          m_FailReason = new UnsupportedAttributeTypeException(
956                              createMessage("Cannot handle date " + errorStr + "!"));
957          result = false;
958        }
959        break;
960
961      case Attribute.STRING:
962        if (isClass)
963          cap = Capability.STRING_CLASS;
964        else
965          cap = Capability.STRING_ATTRIBUTES;
966       
967        if (!handles(cap)) {
968          m_FailReason = new UnsupportedAttributeTypeException(
969                              createMessage("Cannot handle string " + errorStr + "!"));
970          result = false;
971        }
972        break;
973
974      case Attribute.RELATIONAL:
975        if (isClass)
976          cap = Capability.RELATIONAL_CLASS;
977        else
978          cap = Capability.RELATIONAL_ATTRIBUTES;
979       
980        if (!handles(cap)) {
981          m_FailReason = new UnsupportedAttributeTypeException(
982                              createMessage("Cannot handle relational " + errorStr + "!"));
983          result = false;
984        }
985        // attributes in the relation of this attribute must be tested
986        // separately with a different Capabilites object
987        break;
988
989      default:
990        m_FailReason = new UnsupportedAttributeTypeException(
991                            createMessage("Cannot handle unknown attribute type '" 
992                                        + att.type() + "'!"));
993        result = false;
994    }
995   
996    return result;
997  }
998 
999  /**
1000   * Tests the given data, whether it can be processed by the handler,
1001   * given its capabilities. Classifiers implementing the
1002   * <code>MultiInstanceCapabilitiesHandler</code> interface are checked
1003   * automatically for their multi-instance Capabilities (if no bags, then
1004   * only the bag-structure, otherwise only the first bag).
1005   *
1006   * @param data        the data to test
1007   * @return            true if all the tests succeeded
1008   * @see               #test(Instances, int, int)
1009   */
1010  public boolean test(Instances data) {
1011    return test(data, 0, data.numAttributes() - 1);
1012  }
1013 
1014  /**
1015   * Tests a certain range of attributes of the given data, whether it can be
1016   * processed by the handler, given its capabilities. Classifiers
1017   * implementing the <code>MultiInstanceCapabilitiesHandler</code> interface
1018   * are checked automatically for their multi-instance Capabilities (if no
1019   * bags, then only the bag-structure, otherwise only the first bag).
1020   *
1021   * @param data        the data to test
1022   * @param fromIndex   the range of attributes - start (incl.)
1023   * @param toIndex     the range of attributes - end (incl.)
1024   * @return            true if all the tests succeeded
1025   * @see               MultiInstanceCapabilitiesHandler
1026   * @see               #m_InstancesTest
1027   * @see               #m_MissingValuesTest
1028   * @see               #m_MissingClassValuesTest
1029   * @see               #m_MinimumNumberInstancesTest
1030   */
1031  public boolean test(Instances data, int fromIndex, int toIndex) {
1032    int                 i;
1033    int                 n;
1034    int                 m;
1035    Attribute           att;
1036    Instance            inst;
1037    boolean             testClass;
1038    Capabilities        cap;
1039    boolean             missing;
1040    Iterator            iter;
1041   
1042    // shall we test the data?
1043    if (!m_InstancesTest)
1044      return true;
1045   
1046    // no Capabilities? -> warning
1047    if (    (m_Capabilities.size() == 0) 
1048         || ((m_Capabilities.size() == 1) && handles(Capability.NO_CLASS)) )
1049      System.err.println(createMessage("No capabilities set!"));
1050   
1051    // any attributes?
1052    if (toIndex - fromIndex < 0) {
1053      m_FailReason = new WekaException(
1054                          createMessage("No attributes!"));
1055      return false;
1056    }
1057
1058    // do wee need to test the class attribute, i.e., is the class attribute
1059    // within the range of attributes?
1060    testClass =    (data.classIndex() > -1) 
1061                && (data.classIndex() >= fromIndex)
1062                && (data.classIndex() <= toIndex);
1063   
1064    // attributes
1065    for (i = fromIndex; i <= toIndex; i++) {
1066      att = data.attribute(i);
1067     
1068      // class is handled separately
1069      if (i == data.classIndex())
1070        continue;
1071     
1072      // check attribute types
1073      if (!test(att))
1074        return false;
1075    }
1076
1077    // class
1078    if (!handles(Capability.NO_CLASS) && (data.classIndex() == -1)) {
1079      m_FailReason = new UnassignedClassException(
1080          createMessage("Class attribute not set!"));
1081      return false;
1082    }
1083     
1084    // special case: no class attribute can be handled
1085    if (handles(Capability.NO_CLASS) && (data.classIndex() > -1)) {
1086      cap  = getClassCapabilities();
1087      cap.disable(Capability.NO_CLASS);
1088      iter = cap.capabilities();
1089      if (!iter.hasNext()) {
1090        m_FailReason = new WekaException(
1091            createMessage("Cannot handle any class attribute!"));
1092        return false;
1093      }
1094    }
1095     
1096    if (testClass && !handles(Capability.NO_CLASS)) {
1097      att = data.classAttribute();
1098      if (!test(att, true))
1099        return false;
1100
1101      // special handling of RELATIONAL class
1102      // TODO: store additional Capabilities for this case
1103     
1104      // missing class labels
1105      if (m_MissingClassValuesTest) {
1106        if (!handles(Capability.MISSING_CLASS_VALUES)) {
1107          for (i = 0; i < data.numInstances(); i++) {
1108            if (data.instance(i).classIsMissing()) {
1109              m_FailReason = new WekaException(
1110                  createMessage("Cannot handle missing class values!"));
1111              return false;
1112            }
1113          }
1114        }
1115        else {
1116          if (m_MinimumNumberInstancesTest) {
1117            int hasClass = 0;
1118           
1119            for (i = 0; i < data.numInstances(); i++) {
1120              if (!data.instance(i).classIsMissing())
1121                hasClass++;
1122            }
1123           
1124            // not enough instances with class labels?
1125            if (hasClass < getMinimumNumberInstances()) {
1126              m_FailReason = new WekaException(
1127                  createMessage("Not enough training instances with class labels (required: " 
1128                      + getMinimumNumberInstances() 
1129                      + ", provided: " 
1130                      + hasClass + ")!"));
1131              return false;
1132            }
1133          }
1134        }
1135      }
1136    }
1137
1138    // missing values
1139    if (m_MissingValuesTest) {
1140      if (!handles(Capability.MISSING_VALUES)) {
1141        missing = false;
1142        for (i = 0; i < data.numInstances(); i++) {
1143          inst = data.instance(i);
1144         
1145          if (inst instanceof SparseInstance) {
1146            for (m = 0; m < inst.numValues(); m++) {
1147              n = inst.index(m);
1148             
1149              // out of scope?
1150              if (n < fromIndex)
1151                continue;
1152              if (n > toIndex)
1153                break;
1154
1155              // skip class
1156              if (n == inst.classIndex())
1157                continue;
1158             
1159
1160              if (inst.isMissing(n)) {
1161                missing = true;
1162                break;
1163              }
1164            }
1165          }
1166          else {
1167            for (n = fromIndex; n <= toIndex; n++) {
1168              // skip class
1169              if (n == inst.classIndex())
1170                continue;
1171
1172              if (inst.isMissing(n)) {
1173                missing = true;
1174                break;
1175              }
1176            }
1177          }
1178         
1179          if (missing) {
1180            m_FailReason = new NoSupportForMissingValuesException(
1181                createMessage("Cannot handle missing values!"));
1182            return false;
1183          }
1184        }
1185      }
1186    }
1187   
1188    // instances
1189    if (m_MinimumNumberInstancesTest) {
1190      if (data.numInstances() < getMinimumNumberInstances()) {
1191        m_FailReason = new WekaException(
1192            createMessage("Not enough training instances (required: " 
1193                + getMinimumNumberInstances() 
1194                + ", provided: " 
1195                + data.numInstances() + ")!"));
1196        return false;
1197      }
1198    }
1199
1200    // Multi-Instance? -> check structure (regardless of attribute range!)
1201    if (handles(Capability.ONLY_MULTIINSTANCE)) {
1202      // number of attributes?
1203      if (data.numAttributes() != 3) {
1204        m_FailReason = new WekaException(
1205                            createMessage("Incorrect Multi-Instance format, must be 'bag-id, bag, class'!"));
1206        return false;
1207      }
1208     
1209      // type of attributes and position of class?
1210      if (    !data.attribute(0).isNominal() 
1211           || !data.attribute(1).isRelationValued() 
1212           || (data.classIndex() != data.numAttributes() - 1) ) {
1213        m_FailReason = new WekaException(
1214            createMessage("Incorrect Multi-Instance format, must be 'NOMINAL att, RELATIONAL att, CLASS att'!"));
1215        return false;
1216      }
1217
1218      // check data immediately
1219      if (getOwner() instanceof MultiInstanceCapabilitiesHandler) {
1220        MultiInstanceCapabilitiesHandler handler = (MultiInstanceCapabilitiesHandler) getOwner();
1221        cap = handler.getMultiInstanceCapabilities();
1222        boolean result;
1223        if (data.numInstances() > 0)
1224          result = cap.test(data.attribute(1).relation(0));
1225        else
1226          result = cap.test(data.attribute(1).relation());
1227       
1228        if (!result) {
1229          m_FailReason = cap.m_FailReason;
1230          return false;
1231        }
1232      }
1233    }
1234   
1235    // passed all tests!
1236    return true;
1237  }
1238
1239  /**
1240   * tests the given attribute by calling the test(Attribute,boolean) method
1241   * and throws an exception if the test fails. The method assumes that the
1242   * specified attribute is not the class attribute.
1243   *
1244   * @param att         the attribute to test
1245   * @throws Exception  in case the attribute doesn't pass the tests
1246   * @see               #test(Attribute,boolean)
1247   */
1248  public void testWithFail(Attribute att) throws Exception {
1249    test(att, false);
1250  }
1251
1252  /**
1253   * tests the given attribute by calling the test(Attribute,boolean) method
1254   * and throws an exception if the test fails.
1255   *
1256   * @param att         the attribute to test
1257   * @param isClass     whether this attribute is the class attribute
1258   * @throws Exception  in case the attribute doesn't pass the tests
1259   * @see               #test(Attribute,boolean)
1260   */
1261  public void testWithFail(Attribute att, boolean isClass) throws Exception {
1262    if (!test(att, isClass))
1263      throw m_FailReason;
1264  }
1265
1266  /**
1267   * tests the given data by calling the test(Instances,int,int) method and
1268   * throws an exception if the test fails.
1269   *
1270   * @param data        the data to test
1271   * @param fromIndex   the range of attributes - start (incl.)
1272   * @param toIndex     the range of attributes - end (incl.)
1273   * @throws Exception  in case the data doesn't pass the tests
1274   * @see               #test(Instances,int,int)
1275   */
1276  public void testWithFail(Instances data, int fromIndex, int toIndex) throws Exception {
1277    if (!test(data, fromIndex, toIndex))
1278      throw m_FailReason;
1279  }
1280
1281  /**
1282   * tests the given data by calling the test(Instances) method and throws
1283   * an exception if the test fails.
1284   *
1285   * @param data        the data to test
1286   * @throws Exception  in case the data doesn't pass the tests
1287   * @see               #test(Instances)
1288   */
1289  public void testWithFail(Instances data) throws Exception {
1290    if (!test(data))
1291      throw m_FailReason;
1292  }
1293 
1294  /**
1295   * returns a string representation of the capabilities
1296   *
1297   * @return    a string representation of this object
1298   */
1299  public String toString() {
1300    Vector<Capability>          sorted;
1301    StringBuffer        result;
1302   
1303    result = new StringBuffer();
1304
1305    // capabilities
1306    sorted = new Vector<Capability>(m_Capabilities);
1307    Collections.sort(sorted);
1308    result.append("Capabilities: " + sorted.toString() + "\n");
1309
1310    // dependencies
1311    sorted = new Vector<Capability>(m_Dependencies);
1312    Collections.sort(sorted);
1313    result.append("Dependencies: " + sorted.toString() + "\n");
1314   
1315    // other stuff
1316    result.append("min # Instance: " + getMinimumNumberInstances() + "\n");
1317   
1318    return result.toString();
1319  }
1320 
1321  /**
1322   * turns the capabilities object into source code. The returned source code
1323   * is a block that creates a Capabilities object named 'objectname' and
1324   * enables all the capabilities of this Capabilities object.
1325   *
1326   * @param objectname  the name of the Capabilities object being instantiated
1327   * @return            the generated source code
1328   */
1329  public String toSource(String objectname) {
1330    return toSource(objectname, 0);
1331  }
1332   
1333  /**
1334   * turns the capabilities object into source code. The returned source code
1335   * is a block that creates a Capabilities object named 'objectname' and
1336   * enables all the capabilities of this Capabilities object.
1337   *
1338   * @param objectname  the name of the Capabilities object being instantiated
1339   * @param indent      the number of blanks to indent
1340   * @return            the generated source code
1341   */
1342  public String toSource(String objectname, int indent) {
1343    StringBuffer        result;
1344    String              capsName;
1345    String              capName;
1346    String              indentStr;
1347    int                 i;
1348   
1349    result = new StringBuffer();
1350
1351    capsName = Capabilities.class.getName();
1352    capName  = Capabilities.Capability.class.getName().replaceAll("\\$", ".");
1353   
1354    indentStr = "";
1355    for (i = 0; i < indent; i++)
1356      indentStr += " ";
1357   
1358    // object name
1359    result.append(indentStr + capsName + " " + objectname + " = new " + capsName + "(this);\n");
1360   
1361    // capabilities
1362    result.append("\n");
1363    for (Capability cap: Capability.values()) {
1364      // capability
1365      if (handles(cap))
1366        result.append(
1367            indentStr + objectname + ".enable(" + capName + "." + cap.name() + ");\n");
1368      // dependency
1369      if (hasDependency(cap))
1370        result.append(
1371            indentStr + objectname + ".enableDependency(" + capName + "." + cap.name() + ");\n");
1372    }
1373
1374    // other
1375    result.append("\n");
1376    result.append(
1377        indentStr + objectname + ".setMinimumNumberInstances(" 
1378        + getMinimumNumberInstances() + ");\n");
1379
1380    result.append("\n");
1381   
1382    return result.toString();
1383  }
1384 
1385  /**
1386   * returns a Capabilities object specific for this data. The multi-instance
1387   * capability is not checked as well as the minimum number of instances
1388   * is not set.
1389   *
1390   * @param data        the data to base the capabilities on
1391   * @return            a data-specific capabilities object
1392   * @throws Exception  in case an error occurrs, e.g., an unknown attribute
1393   *                    type
1394   */
1395  public static Capabilities forInstances(Instances data) throws Exception {
1396    return forInstances(data, false);
1397  }
1398 
1399  /**
1400   * returns a Capabilities object specific for this data. The minimum number
1401   * of instances is not set, the check for multi-instance data is optional.
1402   *
1403   * @param data        the data to base the capabilities on
1404   * @param multi       if true then the structure is checked, too
1405   * @return            a data-specific capabilities object
1406   * @throws Exception  in case an error occurrs, e.g., an unknown attribute
1407   *                    type
1408   */
1409  public static Capabilities forInstances(Instances data, boolean multi) throws Exception {
1410    Capabilities        result;
1411    Capabilities        multiInstance;
1412    int                 i;
1413    int                 n;
1414    int                 m;
1415    Instance            inst;
1416    boolean             missing;
1417   
1418    result = new Capabilities(null);
1419   
1420    // class
1421    if (data.classIndex() == -1) {
1422      result.enable(Capability.NO_CLASS);
1423    }
1424    else {
1425      switch (data.classAttribute().type()) {
1426        case Attribute.NOMINAL:
1427          if (data.classAttribute().numValues() == 1)
1428            result.enable(Capability.UNARY_CLASS);
1429          else if (data.classAttribute().numValues() == 2)
1430            result.enable(Capability.BINARY_CLASS);
1431          else
1432            result.enable(Capability.NOMINAL_CLASS);
1433          break;
1434         
1435        case Attribute.NUMERIC:
1436          result.enable(Capability.NUMERIC_CLASS);
1437          break;
1438         
1439        case Attribute.STRING:
1440          result.enable(Capability.STRING_CLASS);
1441          break;
1442         
1443        case Attribute.DATE:
1444          result.enable(Capability.DATE_CLASS);
1445          break;
1446         
1447        case Attribute.RELATIONAL:
1448          result.enable(Capability.RELATIONAL_CLASS);
1449          break;
1450         
1451        default:
1452          throw new UnsupportedAttributeTypeException(
1453              "Unknown class attribute type '" + data.classAttribute() + "'!");
1454      }
1455     
1456      // missing class values
1457      for (i = 0; i < data.numInstances(); i++) {
1458        if (data.instance(i).classIsMissing()) {
1459          result.enable(Capability.MISSING_CLASS_VALUES);
1460          break;
1461        }
1462      }
1463    }
1464   
1465    // attributes
1466    for (i = 0; i < data.numAttributes(); i++) {
1467      // skip class
1468      if (i == data.classIndex())
1469        continue;
1470
1471      switch (data.attribute(i).type()) {
1472        case Attribute.NOMINAL:
1473          result.enable(Capability.UNARY_ATTRIBUTES);
1474          if (data.attribute(i).numValues() == 2)
1475            result.enable(Capability.BINARY_ATTRIBUTES);
1476          else if (data.attribute(i).numValues() > 2)
1477            result.enable(Capability.NOMINAL_ATTRIBUTES);
1478          break;
1479
1480        case Attribute.NUMERIC:
1481          result.enable(Capability.NUMERIC_ATTRIBUTES);
1482          break;
1483               
1484        case Attribute.DATE:
1485          result.enable(Capability.DATE_ATTRIBUTES);
1486          break;
1487
1488        case Attribute.STRING:
1489          result.enable(Capability.STRING_ATTRIBUTES);
1490          break;
1491         
1492        case Attribute.RELATIONAL:
1493          result.enable(Capability.RELATIONAL_ATTRIBUTES);
1494          break;
1495         
1496        default:
1497          throw new UnsupportedAttributeTypeException(
1498              "Unknown attribute type '" + data.attribute(i).type() + "'!");
1499      }
1500    }
1501   
1502    // missing values
1503    missing = false;
1504    for (i = 0; i < data.numInstances(); i++) {
1505      inst = data.instance(i);
1506
1507      if (inst instanceof SparseInstance) {
1508        for (m = 0; m < inst.numValues(); m++) {
1509          n = inst.index(m);
1510
1511          // skip class
1512          if (n == inst.classIndex())
1513            continue;
1514
1515          if (inst.isMissing(n)) {
1516            missing = true;
1517            break;
1518          }
1519        }
1520      }
1521      else {
1522        for (n = 0; n < data.numAttributes(); n++) {
1523          // skip class
1524          if (n == inst.classIndex())
1525            continue;
1526
1527          if (inst.isMissing(n)) {
1528            missing = true;
1529            break;
1530          }
1531        }
1532      }
1533
1534      if (missing) {
1535        result.enable(Capability.MISSING_VALUES);
1536        break;
1537      }
1538    }
1539
1540    // multi-instance data?
1541    if (multi) {
1542      if (    (data.numAttributes() == 3)
1543           && (data.attribute(0).isNominal())           // bag-id
1544           && (data.attribute(1).isRelationValued())    // bag
1545           && (data.classIndex() == data.numAttributes() - 1) ) {
1546        multiInstance = new Capabilities(null);
1547        multiInstance.or(result.getClassCapabilities());
1548        multiInstance.enable(Capability.NOMINAL_ATTRIBUTES);
1549        multiInstance.enable(Capability.RELATIONAL_ATTRIBUTES);
1550        multiInstance.enable(Capability.ONLY_MULTIINSTANCE);
1551        result.assign(multiInstance);
1552      }
1553    }
1554   
1555    return result;
1556  }
1557 
1558  /**
1559   * loads the given dataset and prints the Capabilities necessary to
1560   * process it. <p/>
1561   *
1562   * Valid parameters: <p/>
1563   *
1564   * -file filename <br/>
1565   *  the file to load
1566   * 
1567   * -c index
1568   *  the explicit index of the class attribute (default: none)
1569   *
1570   * @param args        the commandline arguments
1571   * @throws Exception  if something goes wrong
1572   */
1573  public static void main(String[] args) throws Exception {
1574    String              tmpStr;
1575    String              filename;
1576    DataSource          source;
1577    Instances           data;
1578    int                 classIndex;
1579    Capabilities        cap;
1580    Iterator            iter;
1581
1582    if (args.length == 0) {
1583      System.out.println(
1584          "\nUsage: " + Capabilities.class.getName() 
1585          + " -file <dataset> [-c <class index>]\n");
1586      return;
1587    }
1588   
1589    // get parameters
1590    tmpStr = Utils.getOption("file", args);
1591    if (tmpStr.length() == 0)
1592      throw new Exception("No file provided with option '-file'!");
1593    else
1594      filename = tmpStr;
1595
1596    tmpStr = Utils.getOption("c", args);
1597    if (tmpStr.length() != 0) {
1598      if (tmpStr.equals("first"))
1599        classIndex = 0;
1600      else if (tmpStr.equals("last"))
1601        classIndex = -2;  // last
1602      else
1603        classIndex = Integer.parseInt(tmpStr) - 1;
1604    }
1605    else {
1606      classIndex = -3;  // not set
1607    }
1608   
1609    // load data
1610    source = new DataSource(filename);
1611    if (classIndex == -3)
1612      data = source.getDataSet();
1613    else if (classIndex == -2)
1614      data = source.getDataSet(source.getStructure().numAttributes() - 1);
1615    else
1616      data = source.getDataSet(classIndex);
1617
1618    // determine and print capabilities
1619    cap = forInstances(data);
1620    System.out.println("File: " + filename);
1621    System.out.println("Class index: " + ((data.classIndex() == -1) ? "not set" : "" + (data.classIndex() + 1)));
1622    System.out.println("Capabilities:");
1623    iter = cap.capabilities();
1624    while (iter.hasNext())
1625      System.out.println("- " + iter.next());
1626  }
1627 
1628  /**
1629   * Returns the revision string.
1630   *
1631   * @return            the revision
1632   */
1633  public String getRevision() {
1634    return RevisionUtils.extract("$Revision: 5953 $");
1635  }
1636}
Note: See TracBrowser for help on using the repository browser.