001    package org.maltparser.core.syntaxgraph.feature;
002    
003    import java.util.LinkedHashMap;
004    import java.util.Map;
005    
006    import org.maltparser.core.exception.MaltChainedException;
007    import org.maltparser.core.feature.function.AddressFunction;
008    import org.maltparser.core.feature.function.FeatureFunction;
009    import org.maltparser.core.feature.value.AddressValue;
010    import org.maltparser.core.feature.value.FeatureValue;
011    import org.maltparser.core.feature.value.SingleFeatureValue;
012    import org.maltparser.core.io.dataformat.ColumnDescription;
013    import org.maltparser.core.symbol.SymbolTable;
014    import org.maltparser.core.symbol.SymbolTableHandler;
015    import org.maltparser.core.symbol.nullvalue.NullValues.NullValueId;
016    import org.maltparser.core.syntaxgraph.SyntaxGraphException;
017    import org.maltparser.core.syntaxgraph.node.DependencyNode;
018    
019    public class NumOfFeature implements FeatureFunction {
020            public enum NumOfRelation {
021                    LDEP, RDEP, DEP
022            };
023            protected AddressFunction addressFunction;
024            protected SymbolTableHandler tableHandler;
025            protected SymbolTable table;
026            protected SingleFeatureValue featureValue;
027            protected NumOfRelation numOfRelation;
028            protected String numOfRelationName;
029            protected String normalizationString;
030            protected Map<Integer,String> normalization;
031            
032            public NumOfFeature(SymbolTableHandler tableHandler) throws MaltChainedException {
033                    super();
034                    featureValue = new SingleFeatureValue(this);
035                    setTableHandler(tableHandler);
036                    normalization = new LinkedHashMap<Integer,String>();
037            }
038            
039            /**
040             * Initialize the distance feature function
041             * 
042             * @param arguments an array of arguments with the type returned by getParameterTypes()
043             * @throws MaltChainedException
044             */
045            public void initialize(Object[] arguments) throws MaltChainedException {
046                    if (arguments.length != 3) {
047                            throw new SyntaxGraphException("Could not initialize NumOfFeature: number of arguments are not correct. ");
048                    }
049                    // Checks that the two arguments are address functions
050                    if (!(arguments[0] instanceof AddressFunction)) {
051                            throw new SyntaxGraphException("Could not initialize NumOfFeature: the first argument is not an address function. ");
052                    }
053                    if (!(arguments[1] instanceof java.lang.String)) {
054                            throw new SyntaxGraphException("Could not initialize NumOfFeature: the second argument (relation) is not a string. ");
055                    }
056                    if (!(arguments[2] instanceof java.lang.String)) {
057                            throw new SyntaxGraphException("Could not initialize NumOfFeature: the third argument (normalization) is not a string. ");
058                    }
059                    setAddressFunction((AddressFunction)arguments[0]);
060                    setNumOfRelation((String)arguments[1]);
061                    
062                    // Creates a symbol table called "NUMOF" using one null value
063                    setSymbolTable(tableHandler.addSymbolTable("NUMOF", ColumnDescription.INPUT, "one"));
064                    normalizationString = (String)arguments[2];
065                    String[] items  = normalizationString.split("\\|");
066                    
067                    if (items.length <= 0 || !items[0].equals("0")) {
068                            throw new SyntaxGraphException("Could not initialize NumOfFeature ("+this+"): the third argument (normalization) must contain a list of integer values separated with | and the first element must be 0.");
069                    }
070                    int tmp = -1;
071                    for (int i = 0; i < items.length; i++) {
072                            int v;
073                            try {
074                                    v = Integer.parseInt(items[i]);
075                            } catch (NumberFormatException e) {
076                                    throw new SyntaxGraphException("Could not initialize NumOfFeature ("+this+"): the third argument (normalization) must contain a sorted list of integer values separated with |", e);
077                            }
078                            normalization.put(v, ">="+v);
079                            table.addSymbol(">="+v);
080                            if (tmp != -1 && tmp >= v) {
081                                    throw new SyntaxGraphException("Could not initialize NumOfFeature ("+this+"): the third argument (normalization) must contain a sorted list of integer values separated with |");
082                            }
083                            tmp = v;
084                    }
085            }
086            
087            /**
088             * Returns an array of class types used by the feature extraction system to invoke initialize with
089             * correct arguments.
090             * 
091             * @return an array of class types
092             */
093            public Class<?>[] getParameterTypes() {
094                    Class<?>[] paramTypes = { org.maltparser.core.feature.function.AddressFunction.class, 
095                                                                      java.lang.String.class,
096                                                                      java.lang.String.class};
097                    return paramTypes; 
098            }
099            
100            /**
101             * Returns the string representation of the integer <code>code</code> according to the numof feature function. 
102             * 
103             * @param code the integer representation of the symbol
104             * @return the string representation of the integer <code>code</code> according to the numof feature function.
105             * @throws MaltChainedException
106             */
107            public String getSymbol(int code) throws MaltChainedException {
108                    return table.getSymbolCodeToString(code);
109            }
110            
111            /**
112             * Returns the integer representation of the string <code>symbol</code> according to the numof feature function.
113             * 
114             * @param symbol the string representation of the symbol
115             * @return the integer representation of the string <code>symbol</code> according to the numof feature function.
116             * @throws MaltChainedException
117             */
118            public int getCode(String symbol) throws MaltChainedException {
119                    return table.getSymbolStringToCode(symbol);
120            }
121            
122            /**
123             * Cause the numof feature function to update the cardinality of the feature value.
124             * 
125             * @throws MaltChainedException
126             */
127            public void updateCardinality() {
128                    featureValue.setCardinality(table.getValueCounter()); 
129            }
130            
131            /**
132             * Cause the feature function to update the feature value.
133             * 
134             * @throws MaltChainedException
135             */
136            public void update() throws MaltChainedException {
137                    // Retrieve the address value 
138                    final AddressValue arg1 = addressFunction.getAddressValue();
139                    // if arg1 or arg2 is null, then set a NO_NODE null value as feature value
140                    if (arg1.getAddress() == null ) { 
141                            featureValue.setCode(table.getNullValueCode(NullValueId.NO_NODE));
142                            featureValue.setSymbol(table.getNullValueSymbol(NullValueId.NO_NODE));
143                            featureValue.setKnown(true);
144                            featureValue.setNullValue(true);                        
145                    } else {
146                            // Unfortunately this method takes a lot of time  arg1.getAddressClass().asSubclass(org.maltparser.core.syntaxgraph.node.DependencyNode.class);
147                            // Cast the address arguments to dependency nodes
148                            final DependencyNode node = (DependencyNode)arg1.getAddress();
149                            int numof = 0;
150                            if (numOfRelation == NumOfRelation.DEP) {
151                                    numof = node.getLeftDependentCount() +  node.getRightDependentCount();
152                            } else if (numOfRelation == NumOfRelation.LDEP) {
153                                    numof = node.getLeftDependentCount();
154                            } else if (numOfRelation == NumOfRelation.RDEP) {
155                                    numof = node.getRightDependentCount();
156                            } 
157                            int lower = -1;
158                            boolean f = false;
159                            for (Integer upper : normalization.keySet()) {
160                                    if (numof >= lower && numof < upper) {
161                                            featureValue.setCode(table.getSymbolStringToCode(normalization.get(lower)));
162                                            featureValue.setSymbol(normalization.get(lower));
163                                            f = true;
164                                            break;
165                                    }
166                                    lower = upper;
167                            }
168                            if (f == false) {
169                                    featureValue.setCode(table.getSymbolStringToCode(normalization.get(lower)));
170                                    featureValue.setSymbol(normalization.get(lower));
171                            }
172                            // Tells the feature value that the feature is known and is not a null value
173                            featureValue.setKnown(true);
174                            featureValue.setNullValue(false);
175                    }
176            }
177            
178            public void setNumOfRelation(String numOfRelationName) {
179                    this.numOfRelationName = numOfRelationName;
180                    numOfRelation = NumOfRelation.valueOf(numOfRelationName.toUpperCase());
181            }
182            
183            public NumOfRelation getNumOfRelation() {
184                    return numOfRelation;
185            }
186            
187            /**
188             * Returns the feature value
189             * 
190             * @return the feature value
191             */
192            public FeatureValue getFeatureValue() {
193                    return featureValue;
194            }
195            
196            /**
197             * Returns the symbol table used by the numof feature function
198             * 
199             * @return the symbol table used by the numof feature function
200             */
201            public SymbolTable getSymbolTable() {
202                    return table;
203            }
204            
205            /**
206             * Returns the address function 
207             * 
208             * @return the address function 
209             */
210            public AddressFunction getAddressFunction() {
211                    return addressFunction;
212            }
213    
214    
215            /**
216             * Sets the address function 
217             * 
218             * @param addressFunction a address function 
219             */
220            public void setAddressFunction(AddressFunction addressFunction) {
221                    this.addressFunction = addressFunction;
222            }
223            
224            /**
225             * Sets the symbol table handler
226             * 
227             * @param tableHandler a symbol table handler
228             */
229            public void setTableHandler(SymbolTableHandler tableHandler) {
230                    this.tableHandler = tableHandler;
231            }
232    
233            /**
234             * Sets the symbol table used by the numof feature function
235             * 
236             * @param table
237             */
238            public void setSymbolTable(SymbolTable table) {
239                    this.table = table;
240            }
241            
242            public boolean equals(Object obj) {
243                    if (this == obj)
244                            return true;
245                    if (obj == null)
246                            return false;
247                    if (getClass() != obj.getClass())
248                            return false;
249                    return obj.toString().equals(this.toString());
250            }
251            
252            public int hashCode() {
253                    return 217 + (null == toString() ? 0 : toString().hashCode());
254            }
255            
256            public String toString() {
257                    final StringBuilder sb = new StringBuilder();
258                    sb.append("NumOf(");
259                    sb.append(addressFunction.toString());
260                    sb.append(", ");
261                    sb.append(numOfRelationName);
262                    sb.append(", ");
263                    sb.append(normalizationString);
264                    sb.append(')');
265                    return sb.toString();
266            }
267    }