View Javadoc

1   package cz.cuni.amis.pogamut.sposh.elements;
2   
3   import cz.cuni.amis.pogamut.shady.ArgString;
4   import cz.cuni.amis.pogamut.sposh.executor.ParamInfo;
5   import java.lang.reflect.Array;
6   import java.util.regex.Matcher;
7   import java.util.regex.Pattern;
8   
9   /**
10   * Class representing value and its properties in posh plan. Value in plan can
11   * be e.g boolean or string and they have some properties. Methods of this class
12   * take an object and perform some test on it (e.g. if you pass {@link Double}
13   * with value 0.0 to {@link #isFalse(java.lang.Object) }, it will return true).
14   *
15   * Most important part of this class are methods {@link #compare(java.lang.Object, java.lang.Object)
16   * } and {@link #equal(java.lang.Object, java.lang.Object) }.
17   *
18   * @author Honza
19   */
20  public class Result {
21  
22      /**
23       * Is the value false? null is false Boolean false is false Number 0 is
24       * false Arrays of size 0 is false, otherwise value is true
25       *
26       * @see http://docs.python.org/library/stdtypes.html#truth-value-testing
27       * @return true if value is null, value convertable to number and 0
28       */
29      public static boolean isFalse(Object value) {
30          if (value == null) {
31              return true;
32          }
33          if (isBoolean(value)) {
34              return !getBoolean(value);
35          }
36          if (isNumber(value)) {
37              return getNumber(value).doubleValue() == 0;
38          }
39          if (value.getClass().isArray()) {
40              return Array.getLength(value) == 0;
41          }
42          return false;
43      }
44  
45      /**
46       * Is value true? Basically negate {@link Result#isFalse(java.lang.Object)
47       * }.
48       *
49       * @param value value for which we want the information.
50       */
51      public static boolean isTrue(Object value) {
52          return !isFalse(value);
53      }
54  
55      public static boolean isNumber(Object value) {
56          return value instanceof Number;
57      }
58  
59      public static Number getNumber(Object value) {
60          assert isNumber(value);
61          return (Number) value;
62      }
63  
64      /**
65       * Is value instance of {@link Boolean} class?
66       *
67       * @param value value to be tested
68       * @return true if it is instance of {@link Boolean}
69       */
70      public static boolean isBoolean(Object value) {
71          return value instanceof Boolean;
72      }
73  
74      /**
75       * Get boolean value from {@link Boolean} class.
76       *
77       * @param value (value must be instance of {@link Boolean) class.
78       * @return value of boolean.
79       */
80      public static boolean getBoolean(Object value) {
81          assert isBoolean(value);
82          return ((Boolean) value).booleanValue();
83      }
84  
85      /**
86       * Parse string from posh plan and convert it to object. Rules of parsing:
87       * "nil" - null "true"/"false" - boolean int number - integer double number
88       * - double otherwise string
89       *
90       * @param valueString string that will be parsed to object NOT NULL.
91       * @return created object
92       */
93      public static Object parseValue(String valueString) throws ParseException {
94          // TODO: Maytbe reuse the parser instead of duplicating the code
95          assert valueString != null;
96  
97          if ("nil".equalsIgnoreCase(valueString)) {
98              return null;
99          }
100 
101         if (valueString.equalsIgnoreCase(Boolean.TRUE.toString())) {
102             return Boolean.TRUE;
103         }
104 
105         if (valueString.equalsIgnoreCase(Boolean.FALSE.toString())) {
106             return Boolean.FALSE;
107         }
108 
109         try {
110             return Integer.parseInt(valueString);
111         } catch (NumberFormatException ex) {
112         }
113 
114         try {
115             return Double.parseDouble(valueString);
116         } catch (NumberFormatException ex) {
117         }
118 
119         if (valueString.length() >= 2 && valueString.startsWith("\"") && valueString.endsWith("\"")) {
120             String unquotedString = valueString.substring(1, valueString.length() - 1);
121             try {
122                 return ArgString.unescape(unquotedString);
123             } catch (cz.cuni.amis.pogamut.shady.ParseException ex) {
124                 throw new ParseException(ex.getMessage());
125             }
126         }
127         if (valueString.length() >= 2 && valueString.startsWith("'")) {
128             String enumString = valueString.substring(1);
129             if (!enumString.matches("[a-zA-Z_]([a-zA-Z_0-9])*(.[a-zA-Z_]([a-zA-Z_0-9])*)*")) {
130                 throw new ParseException(enumString + " is not a valid enum name.");
131             }
132             return new EnumValue(enumString);
133         }
134         throw new ParseException("No good type from " + valueString);
135     }
136 
137     /**
138      * Get numerical representation for object derived from class {@link Number}
139      * and from {@link Boolean} (true = 1, false = 0)
140      *
141      * @param value
142      * @return numerical value of value.
143      */
144     public static double getNumerical(Object value) {
145         if (isBoolean(value)) {
146             return getBoolean(value) ? 1 : 0;
147         }
148 
149         return getNumber(value).doubleValue();
150     }
151 
152     /**
153      * Is value numerical(either number or boolean)?
154      *
155      * @param value value to check if it is numerical
156      */
157     public static boolean isNumerical(Object value) {
158         return isNumber(value) || isBoolean(value);
159     }
160 
161     /**
162      * Are two operands equal? Comparison is based on rules outlines in
163      * http://docs.python.org/reference/expressions.html#notin
164      *
165      * @param operand1
166      * @param operand2
167      * @return
168      */
169     public static boolean equal(Object operand1, Object operand2) {
170         // I can compare them numericly
171         if (isNumerical(operand1) && isNumerical(operand2)) {
172             double op1 = getNumerical(operand1);
173             double op2 = getNumerical(operand2);
174             return op1 == op2;
175         }
176         // otherwise use standard equals
177         return operand1 == null ? operand2 == null : operand1.equals(operand2);
178     }
179 
180     /**
181      * What is the comparison of operand1 and operand2?
182      *
183      * @param operand1
184      * @param operand2
185      * @return a negative integer, zero, or a positive integer as this object is
186      * less than, equal to, or greater than the specified object.
187      */
188     public static int compare(Object operand1, Object operand2) {
189         // I can compare them numericly
190         if (isNumerical(operand1) && isNumerical(operand2)) {
191             double op1 = getNumerical(operand1);
192             double op2 = getNumerical(operand2);
193             int sig = (int) Math.signum(op1 - op2);
194             return sig;
195         }
196         // otherwise use comparable
197         if (operand1 == null) {
198             if (operand2 == null) {
199                 return 0;
200             }
201             // two objects are not equal, but HOW? we have null and something.
202             throw new IllegalArgumentException("I can't compare " + operand1 + " with " + operand2);
203         }
204 
205         Comparable op1 = (Comparable) operand1;
206         Comparable op2 = (Comparable) operand2;
207 
208         return op1.compareTo(op2);
209     }
210 
211     /**
212      * Get string representation of the value that can be used in the lap plan.
213      * Basically opposite of {@link Result#parseValue(java.lang.String) }.
214      *
215      * @param value value to convret
216      * @return string representation that can be used in the plan
217      */
218     public static String toLap(Object value) {
219         if (value == null) {
220             return "nil";
221         }
222         if (value instanceof Character) {
223             return "\"" + value.toString() + "\"";
224         }
225         if (value instanceof String) {
226             return "\"" + value.toString() + "\"";
227         }
228         if (value instanceof EnumValue) {
229             return '\'' + ((EnumValue) value).getName();
230         }
231 
232         return value.toString();
233     }
234     /**
235      * Regexp for parameter name be same as <VARIABLE> PoshParser
236      */
237     public final static String variableNameRegexp = "\\$[a-zA-Z]([_\\-a-zA-Z0-9])*";
238 
239     /**
240      * Check if passed string is variable name. Variable names start with $ and
241      * are followed by characters. Should be same as parser <tt>VARIABLE<tt>
242      * terminal.
243      *
244      * @param possibleVariableName Tested string.
245      * @return true if passed string is varaible name. False otherwise.
246      */
247     public static boolean isVariableName(String possibleVariableName) {
248         return possibleVariableName.matches(variableNameRegexp);
249     }
250 }