View Javadoc

1   /*
2    *  
3    *  Fosstrak LLRP Commander (www.fosstrak.org)
4    * 
5    *  Copyright (C) 2008 ETH Zurich
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 3 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, see <http://www.gnu.org/licenses/> 
19   *
20   */
21  
22  package org.fosstrak.llrp.commander.util;
23  
24  import java.lang.reflect.Method;
25  import java.util.LinkedList;
26  import java.util.List;
27  import java.util.Observable;
28  import org.llrp.ltk.types.*;
29  import org.llrp.ltkGenerator.generated.FieldDefinition;
30  
31  /**
32   * This class constitutes the interface to llrp message objects. 
33   * All modifications to llrp message objects are performed through this class.
34   * 
35   * Users of this class can register to be notified when changes to the llrp message occur.
36   * 
37   * This class has a reference to the <code>LLRPMessage</code> object and is not implemented 
38   * as a static class, because the implementation of the <code>getParent(...)</code> method
39   * requires to know the root of the object tree.
40   * 
41   * @author Ulrich Etter, ETHZ
42   *
43   */
44  public class LLRPTreeMaintainer extends Observable {
45  	
46  	private final String EMPTY_STRING = "";
47  	
48  	private LLRPMessage root;
49  
50  	public LLRPTreeMaintainer(LLRPMessage root){
51  		this.root = root;
52  	}
53  	
54  	/**
55  	 * Sets the llrp message this <code>LLRPTreeMaintainer</code> shall maintain.
56  	 * 
57  	 * @param root the llrp message <code>LLRPTreeMaintainer</code> shall maintain
58  	 */
59  	public void setRoot(LLRPMessage root){
60  		this.root = root;
61  	}
62  
63  	/**
64  	 * Returns the llrp message associated with this <code>LLRPTreeMaintainer</code>.
65  	 * 
66  	 * @return the llrp message associated with this <code>LLRPTreeMaintainer</code>.
67  	 */
68  	public LLRPMessage getRoot() {
69  		return root;
70  	}
71      
72      /**
73       * Sets the given parameter as child of the given message/parameter. 
74       * The message/parameter will know the parameter under the given name.
75       * 
76       * @param messageOrParameter either a <code>LLRPMessage</code> or a <code>LLRPParameter</code>
77       * @param childName the name of the child
78       * @param child the parameter that should be set as a child
79       */
80      public void setChild(Object messageOrParameter, String childName, LLRPParameter child){
81  		String methodName = "set" + childName;	
82  		Object[] methodArguments = {child};
83  		
84  		for (Method method : messageOrParameter.getClass().getMethods()){
85  			if (method.getName().equals(methodName)){
86  				try {
87  					method.invoke(messageOrParameter, methodArguments);
88  				} catch (Exception e) {
89  					e.printStackTrace();
90  				}
91  				break;
92  			}
93  		}
94  		setChanged();
95  		notifyObservers();
96      }
97      
98      /**
99       * Adds the given parameter to the given parameter list.
100      * 
101      * @param list the parameter list to which the child shall be added
102      * @param child the parameter to add to the parameter list
103      */
104     public void addChild(List<LLRPParameter> list, LLRPParameter child){
105     	list.add(child);
106     	setChanged();
107 		notifyObservers();
108     }
109     
110     /**
111      * Removes the given parameter from the given parameter list.
112      * 
113      * @param list the parameter list from which the child shall be removed
114      * @param child the 
115      */
116     public void removeChild(List<LLRPParameter> list, LLRPParameter child){
117 		for (LLRPParameter parameter : list){
118 			if (parameter == child){
119 				list.remove(parameter);
120 				break;
121 			}
122 		}
123 		setChanged();
124 		notifyObservers();
125     }
126     
127     /**
128      * Returns the child with the given name of the given message/parameter.
129      * 
130      * @param messageOrParameter either a <code>LLRPMessage</code> or a <code>LLRPParameter</code>
131      * @param childName the name of the child.
132      * @return the child to the given name, null if not existing.
133      */
134     public Object getChild(Object messageOrParameter, String childName){
135     	Object child = null;
136     	String methodName = "get" + childName;
137 		if (LLRP.canOccurMultipleTimes(getDefinition(messageOrParameter), childName)){
138 			methodName = methodName + "List";
139 		}
140 		try {
141 			child = messageOrParameter.getClass().getMethod(methodName, new Class[0]).invoke(messageOrParameter, new Object[0]);
142 		} catch (Exception e) {
143 			e.printStackTrace();
144 		}
145     	return child;
146     }
147 	
148 	/**
149 	 * Returns all children of the given tree element that are not null.
150 	 * 
151 	 * @param treeElement either a <code>LLRPMessage</code> or a <code>LLRPParameter</code> 
152 	 * 		or a <code>List&lt;LLRPParameter&gt;</code>
153 	 * @return a list of children to a given tree element.
154 	 */
155 	public List<Object> getNonNullChildren(Object treeElement){
156 		List<Object> children = new LinkedList<Object>();
157 		if (treeElement instanceof LLRPMessage || treeElement instanceof LLRPParameter){
158 			Object messageOrParameterDefinition = getDefinition(treeElement);
159 			List<String> childrenNames = LLRP.getParameterAndChoiceNames(messageOrParameterDefinition);
160 			for (String childName : childrenNames){
161 				Object o = getChild(treeElement, childName);
162 				if (o != null){
163 					children.add(o);
164 				}
165 			}
166 		}
167 		else if (treeElement instanceof List){
168 			children = (List<Object>) treeElement;
169 		}
170 		return children;
171 	}
172 	
173 	/**
174 	 * Returns the parent of the given tree element. 
175 	 * If the given element is the root of the tree,
176 	 * <code>Null</code> is returned.
177 	 * 
178 	 * This implementation searches the whole message object tree
179 	 * for the given tree element (starting at the root). This is
180 	 * done, because LTKJava does not provide references from children
181 	 * to their parents (i.e. from sub-parameters to parameters).
182 	 * 
183 	 * @param treeElement either a <code>LLRPMessage</code> or a <code>LLRPParameter</code> 
184 	 * 		or a <code>List&lt;LLRPParameter&gt;</code>
185 	 * @return the parent of a given element in the tree, null if it is the root.
186 	 */
187 	public Object getParent(Object treeElement){
188 		return findParent(root, treeElement);
189 	}
190 	
191 	private Object findParent(Object ancestor, Object treeElement){
192 		Object parent = null;
193 		if (treeElement instanceof LLRPMessage){
194 			// message doesn't have a parent
195 			parent = null;
196 		}
197 		else{
198 			List<Object> children = getNonNullChildren(ancestor);			
199 			if (children.size() == 0){
200 				parent = null;
201 			}
202 			else {
203 				for (Object o : children){
204 						if (o == treeElement){
205 							parent = ancestor;
206 							break;
207 						}
208 						else{
209 							parent = findParent(o, treeElement);
210 							if (parent != null){
211 								break;
212 							}
213 						}
214 				}
215 			}
216 		}
217 		return parent;
218 	}
219 	
220 	/**
221 	 * Returns the field with the given name of the given message/parameter
222 	 * 
223 	 * @param messageOrParameter either a <code>LLRPMessage</code> or a <code>LLRPParameter</code>
224 	 * @param fieldName
225 	 * @return the field with the given name of the given message/parameter.
226 	 */
227 	public LLRPType getField(Object messageOrParameter, String fieldName) {
228 		String methodName = "get" + fieldName;
229 		LLRPType field = null;
230 		try {
231 			field = (LLRPType) messageOrParameter.getClass().getMethod(methodName, new Class[0]).invoke(messageOrParameter, new Object[0]);
232 		} catch (Exception e) {
233 			e.printStackTrace();
234 		}
235 		return field;
236 	}
237 	
238 	/**
239 	 * Sets the field with the given name of the given message/parameter to the given value.
240 	 * 
241 	 * @param messageOrParameter either a <code>LLRPMessage</code> or a <code>LLRPParameter</code>
242 	 * @param fieldName
243 	 * @param fieldValue
244 	 */
245 	public void setField(Object messageOrParameter, String fieldName, LLRPType fieldValue){
246 		String methodName = "set" + fieldName;
247 		Object[] methodArguments = {fieldValue};
248 		
249 		for (Method method : messageOrParameter.getClass().getMethods()){
250 			if (method.getName().equals(methodName)){
251 				try {
252 					method.invoke(messageOrParameter, methodArguments);
253 				} catch (Exception e) {
254 					e.printStackTrace();
255 				}
256 				break;
257 			}
258 		}
259 		setChanged();
260 		notifyObservers();
261 	}
262 	
263 	/**
264 	 * Returns all fields of the given message/parameter.
265 	 * 
266 	 * @param messageOrParameter either a <code>LLRPMessage</code> or a <code>LLRPParameter</code>
267 	 * @return a list of llrp fields to a given parameter or message.
268 	 */
269 	public List<LLRPType> getFields(Object messageOrParameter){
270 		LinkedList<LLRPType> fields = new LinkedList<LLRPType>();
271     	List<FieldDefinition> fieldDefinitions = LLRP.getFieldDefinitions(getDefinition(messageOrParameter));
272     	for (int i = 0; i < fieldDefinitions.size(); i++){
273 			FieldDefinition fieldDefinition = fieldDefinitions.get(i);
274 			fields.add(getField(messageOrParameter, fieldDefinition.getName()));
275 		}
276 		return fields;
277     }
278 
279 	
280 	/**
281 	 * Returns <code>true</code> if the given tree element is valid (including all its descendants), and <code>false</code> otherwise.
282 	 * 
283 	 * @param treeElement either a <code>LLRPMessage</code> or a <code>LLRPParameter</code> 
284 	 * 		or a <code>List&lt;LLRPParameter&gt;</code>
285 	 * @return true if the given tree element is valid, false otherwise.
286 	 */
287 	public boolean isValid(Object treeElement){		
288 		boolean result = isNonRecursivelyValid(treeElement);
289 		List<Object> children = getNonNullChildren(treeElement);
290 		for (Object o : children){
291 			result = result && isValid(o);
292 		}
293 		return result;
294 	}
295 	
296 	/**
297 	 * Returns <code>true</code> if the given tree element is valid (ignoring the validity of its descendants), 
298 	 * and <code>false</code> otherwise.
299 	 * 
300 	 * @param treeElement either a <code>LLRPMessage</code> or a <code>LLRPParameter</code> 
301 	 * 		or a <code>List&lt;LLRPParameter&gt;</code>
302 	 * @return true if the given tree element is valid, false otherwise.
303 	 */
304 	public boolean isNonRecursivelyValid(Object treeElement){
305 		if (treeElement instanceof LLRPMessage || treeElement instanceof LLRPParameter){
306 			return areFieldsValid(treeElement) && areMandatoryParametersPresent(treeElement) && areListsNonRecursivelyValid(treeElement);
307 		}
308 		else if (treeElement instanceof List){
309 			return validateEmptiness((List<LLRPParameter>) treeElement).equals(EMPTY_STRING);
310 		}
311 		return false;
312 	}
313 	
314 	/**
315 	 * Returns all message/parameter descendants of the given tree element which are (non-recursively) invalid 
316 	 * (including the tree element itself).
317 	 * 
318 	 * @param treeElement either a <code>LLRPMessage</code> or a <code>LLRPParameter</code> 
319 	 * 		or a <code>List&lt;LLRPParameter&gt;</code>
320 	 * @return a list of invalid <code>LLRPMessage</code>s and <code>LLRPParameter</code>s
321 	 */
322 	public List<Object> getNonRecursivelyInvalidMessageOrParameterDescendants(Object treeElement){
323 		List<Object> result = new LinkedList<Object>();
324 		if (!isNonRecursivelyValid(treeElement) && !(treeElement instanceof List)){
325 			result.add(treeElement);
326 		}
327 		for (Object child : getNonNullChildren(treeElement)){
328 			result.addAll(getNonRecursivelyInvalidMessageOrParameterDescendants(child));
329 		}
330 		return result;
331 	}
332 	
333 	/**
334 	 * Checks whether the field with the given name of the given message/parameter is valid.
335 	 * 
336 	 * @param messageOrParameter either a <code>LLRPMessage</code> or a <code>LLRPParameter</code>
337 	 * @param fieldName
338 	 * @return an error message if the field is not valid, and an empty string otherwise
339 	 */
340 	public String validateField(Object messageOrParameter, String fieldName){
341 		LLRPType field = getField(messageOrParameter, fieldName);
342 		if (field == null){
343 			return LLRPConstraints.NULL_FIELD_ERROR_MESSAGE;
344 		}
345 		else{		
346 			int fieldValue = 0;
347 			if (field instanceof LLRPNumberType){
348 				fieldValue = ((LLRPNumberType) field).intValue();
349 			}
350 			else if (field instanceof TwoBitField){
351 				fieldValue = ((TwoBitField) field).intValue();
352 			}
353 			else {
354 				return EMPTY_STRING;
355 			}
356 			LLRPRangeConstraint[] constraints = LLRPConstraints.rangeConstraints;
357 			for (int i = 0; i < constraints.length; i++){
358 				if (getName(messageOrParameter).equals(constraints[i].getMessageOrParameterName())
359 						&& fieldName.equals(constraints[i].getFieldName())){
360 					String enumerationName = constraints[i].getPreconditionedEnumerationName();
361 					if (enumerationName != null){
362 						LLRPEnumeration enumeration = (LLRPEnumeration) getField(messageOrParameter, enumerationName);
363 						if (enumeration != null &&
364 								enumeration.toString().equals(constraints[i].getPreconditionedEnumerationValue())){
365 							if (! constraints[i].isSatisfied(fieldValue)){
366 								return constraints[i].getErrorMessage();
367 							}
368 						}
369 					}
370 					else {
371 						if (! constraints[i].isSatisfied(fieldValue)){
372 							return constraints[i].getErrorMessage();
373 						}
374 					}
375 				}
376 			}
377 			return EMPTY_STRING;
378 		}
379 	}
380 	
381 	/**
382 	 * Checks whether the child is present when it has to be present.
383 	 * 
384 	 * @param messageOrParameter either a <code>LLRPMessage</code> or a <code>LLRPParameter</code>
385 	 * @param childName
386 	 * @return an error message if the child is not present illegally, and an empty string otherwise
387 	 */
388 	public String validateChildPresence(Object messageOrParameter, String childName){
389 		if (messageOrParameter instanceof LLRPMessage || messageOrParameter instanceof LLRPParameter){
390 			Object messageOrParameterDefinition = getDefinition(messageOrParameter);
391 			if (getChild(messageOrParameter, childName) == null){
392 				if (LLRP.mustOccurAtLeastOnce(messageOrParameterDefinition, childName)){
393 						return LLRPConstraints.MISSING_PARAMETER_ERROR_MESSAGE;
394 				}
395 				else {
396 					
397 					LLRPPresenceConstraint[] constraints = LLRPConstraints.presenceConstraints;
398 					for (int i = 0; i < constraints.length; i++){
399 						if (getName(messageOrParameter).equals(constraints[i].getMessageOrParameterName())
400 								&& childName.equals(constraints[i].getSubparameterName())){
401 							String enumerationName = constraints[i].getPreconditionedEnumerationName();
402 							if (enumerationName != null){
403 								LLRPEnumeration enumeration = (LLRPEnumeration) getField(messageOrParameter, enumerationName);
404 								if (enumeration != null &&  
405 										enumeration.toString().equals(constraints[i].getPreconditionedEnumerationValue())){
406 									return constraints[i].getErrorMessage();
407 								}
408 							}
409 						}
410 					}
411 				
412 				}
413 			}
414 		}
415 		return EMPTY_STRING;
416 	}
417 	
418 	/**
419 	 * Checks whether the list is non-empty when it has to be non-empty.
420 	 * 
421 	 * @param list
422 	 * @return an error message if the list is empty illegally, and an empty string otherwise
423 	 */
424 	public String validateEmptiness(List<LLRPParameter> list){
425 		String errorMessage = EMPTY_STRING;
426 		Object parent = getParent(list);
427 		Object messageOrParameterDefinition = getDefinition(parent);
428 		if (LLRP.mustOccurAtLeastOnce(messageOrParameterDefinition, getName(list)) && list.isEmpty()){
429 			errorMessage = LLRPConstraints.EMPTY_LIST_ERROR_MESSAGE;
430 		}
431 		return errorMessage;
432 	}
433 	
434 	/**
435 	 * Returns <code>true</code> if all fields of the given message/parameter are valid, and <code>false</code> otherwise.
436 	 * 
437 	 * @param messageOrParameter either a <code>LLRPMessage</code> or a <code>LLRPParameter</code>
438 	 * @return
439 	 */
440 	private boolean areFieldsValid(Object messageOrParameter) {
441 		boolean result = true;
442 		List<FieldDefinition> fieldDefinitions = LLRP.getFieldDefinitions(getDefinition(messageOrParameter));
443 		for (FieldDefinition fd : fieldDefinitions){
444 			if (!validateField(messageOrParameter, fd.getName()).equals(EMPTY_STRING)){
445 				result = false;
446 			}
447 		}
448 		return result;
449 	}
450 	
451 	/**
452 	 * Returns <code>true</code> if all mandatory parameters of the given message/parameter are present, and <code>false</code> otherwise.
453 	 * 
454 	 * @param messageOrParameter either a <code>LLRPMessage</code> or a <code>LLRPParameter</code>
455 	 * @return
456 	 */
457 	private boolean areMandatoryParametersPresent(Object messageOrParameter){
458 		boolean result = true;
459 		if (messageOrParameter instanceof LLRPMessage || messageOrParameter instanceof LLRPParameter){
460 			Object messageOrParameterDefinition = getDefinition(messageOrParameter);
461 			List<String> childrenNames = LLRP.getParameterAndChoiceNames(messageOrParameterDefinition);
462 			for (String childName : childrenNames){
463 				if (!validateChildPresence(messageOrParameter, childName).equals(EMPTY_STRING)){
464 					result = false;
465 					break;
466 				}
467 			}
468 		}
469 		return result;
470 	}
471 	
472 	/**
473 	 * Returns <code>true</code> if all lists of the given message/parameter are (non-recursively) valid, 
474 	 * and <code>false</code> otherwise.
475 	 * 
476 	 * @param messageOrParameter either a <code>LLRPMessage</code> or a <code>LLRPParameter</code>
477 	 * @return
478 	 */
479 	private boolean areListsNonRecursivelyValid(Object messageOrParameter){
480 		boolean result = true;
481 		Object messageOrParameterDefinition = getDefinition(messageOrParameter);
482 		List<String> childrenNames = LLRP.getParameterAndChoiceNames(messageOrParameterDefinition);
483 		for (String childName : childrenNames){
484 			if (LLRP.canOccurMultipleTimes(messageOrParameterDefinition, childName)){
485 				if (!validateEmptiness((List<LLRPParameter>) getChild(messageOrParameter, childName)).equals(EMPTY_STRING)){
486 					result = false;
487 				}
488 			}
489 		}
490 		return result;
491 	}
492 
493 	/**
494 	 * Returns the name of the given tree element.
495 	 * 
496 	 * @param treeElement either a <code>LLRPMessage</code> or a <code>LLRPParameter</code> 
497 	 * 		or a <code>List&lt;LLRPParameter&gt;</code>
498 	 * @return the name of a given tree element.
499 	 */
500 	public String getName(Object treeElement) {
501 		String name = EMPTY_STRING;
502 		if (treeElement instanceof LLRPMessage){
503 			name = ((LLRPMessage) treeElement).getName();
504 		}
505 		else if (treeElement instanceof LLRPParameter){
506 			name = ((LLRPParameter) treeElement).getName();
507 		}
508 		else if (treeElement instanceof List){		
509 			Object parent = getParent(treeElement);
510 			Object messageOrParameterDefinition = getDefinition(parent);
511 			List<String> childrenNames = LLRP.getParameterAndChoiceNames(messageOrParameterDefinition);
512 			for (String childName : childrenNames){
513 				Object child = getChild(parent, childName);
514 				if (child == treeElement){
515 					name = childName;
516 					break;
517 				}
518 			}
519 		}
520 		return name;
521 	}
522 	
523 	/**
524 	 * Returns the definition of the given message or parameter.
525 	 * 
526 	 * @param messageOrParameter either a <code>LLRPMessage</code> or a <code>LLRPParameter</code>
527 	 * @return either a <code>MessageDefinition</code> or a <code>ParameterDefinition</code>
528 	 */
529 	public Object getDefinition(Object messageOrParameter) {
530 		Object messageOrParameterDefinition = null;
531 		if (messageOrParameter instanceof LLRPMessage){
532 			messageOrParameterDefinition = LLRP.getMessageDefinition(getName(messageOrParameter));
533 		}
534 		else if (messageOrParameter instanceof LLRPParameter){
535 			messageOrParameterDefinition = LLRP.getParameterDefinition(getName(messageOrParameter));
536 		}
537 		return messageOrParameterDefinition;
538 	}
539 }