001: /*
002: * This program is free software; you can redistribute it and/or modify
003: * it under the terms of the GNU General Public License as published by
004: * the Free Software Foundation; either version 2 of the License, or
005: * (at your option) any later version.
006: *
007: * This program is distributed in the hope that it will be useful,
008: * but WITHOUT ANY WARRANTY; without even the implied warranty of
009: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
010: * GNU General Public License for more details.
011: *
012: * You should have received a copy of the GNU General Public License
013: * along with this program; if not, write to the Free Software
014: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
015: */
016:
017: /*
018: * EnsembleLibraryModel.java
019: * Copyright (C) 2006 Robert Jung
020: *
021: */
022:
023: package weka.classifiers;
024:
025: import weka.core.OptionHandler;
026: import weka.core.Utils;
027: import weka.gui.GenericObjectEditor;
028:
029: import java.beans.BeanInfo;
030: import java.beans.IntrospectionException;
031: import java.beans.Introspector;
032: import java.beans.MethodDescriptor;
033: import java.beans.PropertyDescriptor;
034: import java.beans.PropertyEditor;
035: import java.beans.PropertyEditorManager;
036: import java.io.IOException;
037: import java.io.ObjectInputStream;
038: import java.io.ObjectOutputStream;
039: import java.io.Serializable;
040: import java.lang.reflect.InvocationTargetException;
041: import java.lang.reflect.Method;
042:
043: /**
044: * This class is a light wrapper that is meant to represent a
045: * classifier in a classifier library. This simple base class
046: * is intended only to store the class type and the parameters
047: * for a specific "type" of classifier. You will need to
048: * extend this class to add functionality specific to models
049: * that have been trained on data (as we have for Ensemble Selection)
050: *
051: * @author Robert Jung (mrbobjung@gmail.com)
052: * @version $Revision: 1.1 $
053: */
054: public class EnsembleLibraryModel implements Serializable {
055:
056: /** this stores the class of the classifier */
057: //private Class m_ModelClass;
058: /**
059: * This is the serialVersionUID that SHOULD stay the same
060: * so that future modified versions of this class will be
061: * backwards compatible with older model versions.
062: */
063: private static final long serialVersionUID = 7932816660173443200L;
064:
065: /** this is an array of options*/
066: protected Classifier m_Classifier;
067:
068: /** the description of this classifier*/
069: protected String m_DescriptionText;
070:
071: /** this is also saved separately */
072: protected String m_ErrorText;
073:
074: /** a boolean variable tracking whether or not this classifier was
075: * able to be built successfully with the given set of options*/
076: protected boolean m_OptionsWereValid;
077:
078: /** this is stores redundantly to speed up the many operations that
079: frequently need to get the model string representations (like JList
080: renderers) */
081: protected String m_StringRepresentation;
082:
083: /**
084: * Default Constructor
085: */
086: public EnsembleLibraryModel() {
087: super ();
088: }
089:
090: /**
091: * Initializes the class with the given classifier.
092: *
093: * @param classifier the classifier to use
094: */
095: public EnsembleLibraryModel(Classifier classifier) {
096:
097: m_Classifier = classifier;
098:
099: //this may seem redundant and stupid, but it really is a good idea
100: //to cache the stringRepresentation to minimize the amount of work
101: //that needs to be done when these Models are rendered
102: m_StringRepresentation = toString();
103:
104: updateDescriptionText();
105: }
106:
107: /**
108: * This method will attempt to instantiate this classifier with the given
109: * options. If an exception is thrown from the setOptions method of the
110: * classifier then the resulting exception text will be saved in the
111: * description text string.
112: */
113: public void testOptions() {
114:
115: Classifier testClassifier = null;
116: try {
117: testClassifier = ((Classifier) m_Classifier.getClass()
118: .newInstance());
119: } catch (InstantiationException e) {
120: e.printStackTrace();
121: } catch (IllegalAccessException e) {
122: e.printStackTrace();
123: }
124:
125: setOptionsWereValid(true);
126: setErrorText(null);
127: updateDescriptionText();
128:
129: try {
130: testClassifier.setOptions(m_Classifier.getOptions());
131: } catch (Exception e) {
132:
133: setOptionsWereValid(false);
134: setErrorText(e.getMessage());
135: }
136:
137: updateDescriptionText();
138: }
139:
140: /**
141: * Returns the base classifier this library model represents.
142: *
143: * @return the base classifier
144: */
145: public Classifier getClassifier() {
146: return m_Classifier;
147: }
148:
149: /**
150: * getter for the string representation
151: *
152: * @return the string representation
153: */
154: public String getStringRepresentation() {
155: return m_StringRepresentation;
156: }
157:
158: /**
159: * setter for the description text
160: *
161: * @param descriptionText the description
162: */
163: public void setDescriptionText(String descriptionText) {
164: m_DescriptionText = descriptionText;
165: }
166:
167: /**
168: * getter for the string representation
169: *
170: * @return the description
171: */
172: public String getDescriptionText() {
173: return m_DescriptionText;
174: }
175:
176: /**
177: * setter for the error text
178: *
179: * @param errorText the error text
180: */
181: public void setErrorText(String errorText) {
182: this .m_ErrorText = errorText;
183: }
184:
185: /**
186: * getter for the error text
187: *
188: * @return the error text
189: */
190: public String getErrorText() {
191: return m_ErrorText;
192: }
193:
194: /**
195: * setter for the optionsWereValid member variable
196: *
197: * @param optionsWereValid if true, the options were valid
198: */
199: public void setOptionsWereValid(boolean optionsWereValid) {
200: this .m_OptionsWereValid = optionsWereValid;
201: }
202:
203: /**
204: * getter for the optionsWereValid member variable
205: *
206: * @return true if the options were valid
207: */
208: public boolean getOptionsWereValid() {
209: return m_OptionsWereValid;
210: }
211:
212: /**
213: * This method converts the current set of arguments and the
214: * class name to a string value representing the command line
215: * invocation
216: *
217: * @return a string representation of classname and options
218: */
219: public String toString() {
220:
221: String str = m_Classifier.getClass().getName();
222:
223: str += " "
224: + Utils.joinOptions(((OptionHandler) m_Classifier)
225: .getOptions());
226:
227: return str;
228:
229: }
230:
231: /**
232: * getter for the modelClass
233: *
234: * @return the model class
235: */
236: public Class getModelClass() {
237: return m_Classifier.getClass();
238: }
239:
240: /**
241: * getter for the classifier options
242: *
243: * @return the classifier options
244: */
245: public String[] getOptions() {
246: return m_Classifier.getOptions();
247: }
248:
249: /**
250: * Custom serialization method
251: *
252: * @param stream the stream to write the object to
253: * @throws IOException if something goes wrong IO-wise
254: */
255: private void writeObject(ObjectOutputStream stream)
256: throws IOException {
257: //stream.defaultWriteObject();
258: //stream.writeObject(b);
259:
260: //serialize the LibraryModel fields
261:
262: stream.writeObject(m_Classifier);
263:
264: stream.writeObject(m_DescriptionText);
265:
266: stream.writeObject(m_ErrorText);
267:
268: stream.writeObject(new Boolean(m_OptionsWereValid));
269:
270: stream.writeObject(m_StringRepresentation);
271: }
272:
273: /**
274: * Custom serialization method
275: *
276: * @param stream the stream to write to
277: * @throws IOException if something goes wrong IO-wise
278: * @throws ClassNotFoundException if class couldn't be found
279: */
280: private void readObject(ObjectInputStream stream)
281: throws IOException, ClassNotFoundException {
282:
283: m_Classifier = (Classifier) stream.readObject();
284:
285: m_DescriptionText = (String) stream.readObject();
286:
287: m_ErrorText = (String) stream.readObject();
288:
289: m_OptionsWereValid = ((Boolean) stream.readObject())
290: .booleanValue();
291:
292: m_StringRepresentation = (String) stream.readObject();
293: }
294:
295: /**
296: * This method loops through all of the properties of a classifier to
297: * build the html toolTipText that will show all of the property values
298: * for this model.
299: *
300: * Note that this code is copied+adapted from the PropertySheetPanel
301: * class.
302: */
303: public void updateDescriptionText() {
304:
305: String toolTipText = new String("<html>");
306:
307: if (!m_OptionsWereValid) {
308:
309: toolTipText += "<font COLOR=\"#FF0000\">"
310: + "<b>Invalid Model:</b><br>" + m_ErrorText
311: + "<br></font>";
312: }
313:
314: toolTipText += "<TABLE>";
315:
316: PropertyDescriptor properties[] = null;
317: MethodDescriptor methods[] = null;
318:
319: try {
320: BeanInfo bi = Introspector.getBeanInfo(m_Classifier
321: .getClass());
322: properties = bi.getPropertyDescriptors();
323: methods = bi.getMethodDescriptors();
324: } catch (IntrospectionException ex) {
325: System.err.println("LibraryModel: Couldn't introspect");
326: return;
327: }
328:
329: for (int i = 0; i < properties.length; i++) {
330:
331: if (properties[i].isHidden() || properties[i].isExpert()) {
332: continue;
333: }
334:
335: String name = properties[i].getDisplayName();
336: Class type = properties[i].getPropertyType();
337: Method getter = properties[i].getReadMethod();
338: Method setter = properties[i].getWriteMethod();
339:
340: // Only display read/write properties.
341: if (getter == null || setter == null) {
342: continue;
343: }
344:
345: try {
346: Object args[] = {};
347: Object value = getter.invoke(m_Classifier, args);
348:
349: PropertyEditor editor = null;
350: Class pec = properties[i].getPropertyEditorClass();
351: if (pec != null) {
352: try {
353: editor = (PropertyEditor) pec.newInstance();
354: } catch (Exception ex) {
355: // Drop through.
356: }
357: }
358: if (editor == null) {
359: editor = PropertyEditorManager.findEditor(type);
360: }
361: //editors[i] = editor;
362:
363: // If we can't edit this component, skip it.
364: if (editor == null) {
365: continue;
366: }
367: if (editor instanceof GenericObjectEditor) {
368: ((GenericObjectEditor) editor).setClassType(type);
369: }
370:
371: // Don't try to set null values:
372: if (value == null) {
373: continue;
374: }
375:
376: toolTipText += "<TR><TD>" + name + "</TD><TD>"
377: + value.toString() + "</TD></TR>";
378:
379: } catch (InvocationTargetException ex) {
380: System.err.println("Skipping property " + name
381: + " ; exception on target: "
382: + ex.getTargetException());
383: ex.getTargetException().printStackTrace();
384: continue;
385: } catch (Exception ex) {
386: System.err.println("Skipping property " + name
387: + " ; exception: " + ex);
388: ex.printStackTrace();
389: continue;
390: }
391: }
392:
393: toolTipText += "</TABLE>";
394: toolTipText += "</html>";
395: m_DescriptionText = toolTipText;
396: }
397: }
|