View Javadoc

1   /*
2    * Copyright (C) 2004-2006 Christian Siefkes <christian@siefkes.net>.
3    * Development of this software is supported by the German Research Society,
4    * Berlin-Brandenburg Graduate School in Distributed Information Systems
5    * (DFG grant no. GRK 316).
6    *
7    * This program is free software; you can redistribute it and/or modify
8    * it under the terms of the GNU General Public License as published by
9    * the Free Software Foundation; either version 2 of the License, or
10   * (at your option) any later version.
11   *
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with this program; if not, visit
19   * http://www.gnu.org/licenses/gpl.html or write to the Free Software
20   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21   */
22  package de.fu_berlin.ties.classify.feature;
23  
24  import org.apache.commons.lang.StringUtils;
25  import org.apache.commons.lang.builder.ToStringBuilder;
26  import org.dom4j.Element;
27  import org.dom4j.QName;
28  
29  import de.fu_berlin.ties.ProcessingException;
30  import de.fu_berlin.ties.TiesConfiguration;
31  import de.fu_berlin.ties.io.ObjectElement;
32  import de.fu_berlin.ties.io.XMLStorable;
33  import de.fu_berlin.ties.util.Util;
34  import de.fu_berlin.ties.xml.dom.DOMUtils;
35  
36  /***
37   * A feature transformer transforms a feature vector in a suitable way.
38   * Feature transformer can be used for feature selection (discarding irrelevant
39   * features), for combining features etc. Feature transformers can be
40   * <em>chained</em> -- in this case each feature transformer will work on the
41   * featured returned by the previous one.
42   *
43   * @author Christian Siefkes
44   * @version $Revision: 1.11 $, $Date: 2006/10/21 16:03:57 $, $Author: siefkes $
45   */
46  public abstract class FeatureTransformer implements XMLStorable {
47  
48      /***
49       * Name of the main element used for XML serialization.
50       */
51      public static final QName ELEMENT_MAIN =
52          DOMUtils.defaultName("transformer");
53  
54      /***
55       * Configuration key specifying the list of transformers to chain.
56       */
57      public static final String CONFIG_TRANSFORMERS = "transformer.chain";
58  
59      /***
60       * Factory method that delegates to {@link #createTransformer()} using the
61       * {@linkplain TiesConfiguration#CONF standard configuration}.
62       *
63       * @return the last transformer in the created transformer chain; or
64       * <code>null</code> is no transformers are specified (parameter value is
65       * <code>null</code> or empty)
66       * @throws IllegalArgumentException if the value of the
67       * {@link #CONFIG_TRANSFORMERS} key is invalid
68       * @throws ProcessingException if an error occurred while creating the
69       * transformer
70       */
71      public static FeatureTransformer createTransformer()
72      throws IllegalArgumentException, ProcessingException {
73          return createTransformer(TiesConfiguration.CONF);
74      }
75  
76      /***
77       * Factory method that creates a feature transformer based on the
78       * {@link #CONFIG_TRANSFORMERS} key in the provided configuration.
79       * This parameter must contain a list fully specified names of subclasses
80       * of this class) used in a chain.
81       *
82       * <p>Eeach specified transformer must provide a constructor accepting a
83       * preceding {@link FeatureTransformer} as first argument and a
84       * {@link TiesConfiguration} as second argument.
85       *
86       * @param config the configuration to use
87       * @return the last transformer in the created transformer chain; or
88       * <code>null</code> is no transformers are specified (parameter value is
89       * <code>null</code> or empty)
90       * @throws IllegalArgumentException if the value of the
91       * {@link #CONFIG_TRANSFORMERS} key is invalid
92       * @throws ProcessingException if an error occurred while creating the
93       * transformer
94       */
95      public static FeatureTransformer createTransformer(
96              final TiesConfiguration config)
97      throws IllegalArgumentException, ProcessingException {
98          final String[] paramValue = config.getStringArray(CONFIG_TRANSFORMERS);
99          FeatureTransformer transformer = null;
100         int i = 0;
101 
102         try {
103             for (; i < paramValue.length; i++) {
104                 // initialize each of the specified transformers, handing over
105                 // the previous transformer as first single argument and the
106                 // TiesConfiguration as second argument
107                 if (StringUtils.isNotEmpty(paramValue[i])) {
108                     transformer = (FeatureTransformer) Util.createObject(
109                         Class.forName(paramValue[i]),
110                         new Object[] {transformer, config},
111                         new Class[] {FeatureTransformer.class,
112                             TiesConfiguration.class});
113                 }
114             }
115         } catch (ClassNotFoundException cnfe) {
116              // convert and rethrow exception
117              throw new ProcessingException(
118                  "Cannot create transformer chain from key "
119                  + CONFIG_TRANSFORMERS + " because the class " + paramValue[i]
120                  + " is missing: " + cnfe.toString());
121          } catch (InstantiationException ie) {
122              // convert and rethrow exception
123              throw new ProcessingException(
124                  "Cannot create transformer chain from key "
125                  + CONFIG_TRANSFORMERS + " because instantation of the class "
126                  + paramValue[i] + " failed: ", ie);
127         }
128 
129         // return the last transformer in the chain (if any)
130         return transformer;
131     }
132 
133     /***
134      * The preceding transformer used if this transformer is part of a
135      * <em>chain</em>; <code>null</code> otherwise.
136      */
137     private final FeatureTransformer precedingTransformer;
138 
139     /***
140      * Creates a new instance.
141      *
142      * @param precTrans the preceding transformer to use if this transformer
143      * is part of a <em>chain</em>; <code>null</code> otherwise
144      */
145     public FeatureTransformer(final FeatureTransformer precTrans) {
146         super();
147         precedingTransformer = precTrans;
148     }
149 
150     /***
151      * Creates a new instance from an XML element, fulfilling the
152      * recommandation of the {@link XMLStorable} interface.
153      *
154      * @param element the XML element containing the serialized representation
155      * @throws InstantiationException if the given element does not contain
156      * a valid transformer description
157      */
158     public FeatureTransformer(final Element element)
159     throws InstantiationException {
160         // init preceding transformer from nested element if present
161         this((FeatureTransformer) ObjectElement.createNextObject(
162                 element.elementIterator(ELEMENT_MAIN)));
163     }
164 
165     /***
166      * Transforms a feature vector.
167      *
168      * @param orgFeatures the original feature vector to transform
169      * @return a new feature vector containing the transformed features
170      */
171     protected abstract FeatureVector doTransform(
172         final FeatureVector orgFeatures);
173 
174     /***
175      * Returns the preceding transformer used if this transformer is part of a
176      * <em>chain</em>.
177      *
178      * @return the preceding transformer, if any; or <code>null</code> if this
179      * transformer is not part of a chain resp. is the first transformer in a
180      * chain
181      */
182     public FeatureTransformer getPrecedingTransformer() {
183         return precedingTransformer;
184     }
185 
186     /***
187      * {@inheritDoc}
188      */
189     public ObjectElement toElement() {
190         // create main element
191         final ObjectElement result =
192             new ObjectElement(ELEMENT_MAIN, this.getClass());
193 
194         // add preceding transformer as child, if not null
195         if (precedingTransformer != null) {
196             result.add(precedingTransformer.toElement());
197         }
198 
199         return result;
200     }
201 
202     /***
203      * Returns a string representation of this object.
204      *
205      * @return a textual representation
206      */
207     public String toString() {
208         final ToStringBuilder builder =  new ToStringBuilder(this);
209 
210         if (precedingTransformer != null) {
211             builder.append("preceding transformer", precedingTransformer);
212         }
213         return builder.toString();
214     }
215 
216     /***
217      * Transforms a feature vector. This method calls itself on the
218      * {@linkplain #getPrecedingTransformer() preceding transformer} (if any)
219      * prior to delegating to the abstract {@link #doTransform(FeatureVector)}
220      * method.
221      *
222      * @param orgFeatures the original feature vector to transform
223      * @return a new feature vector containing the transformed features
224      */
225     public FeatureVector transform(final FeatureVector orgFeatures) {
226         final FeatureVector result;
227 
228         if (precedingTransformer != null) {
229             // call preceding transformer prior to abstract method
230             result = doTransform(precedingTransformer.transform(orgFeatures));
231         } else {
232             // no preceding transformer -- just call abstract method
233             result = doTransform(orgFeatures);
234         }
235 
236         // store reference to transformation in original feature vector
237         orgFeatures.setTransformed(result);
238         return result;
239     }
240 
241 }