4A Server -  2.0
 All Classes Namespaces Files Functions Variables Enumerator
SugBaseAttribute.java
Go to the documentation of this file.
1 /*
2  * Project: Server for annotations sharing
3  * Author: Ing. Jaroslav Dytrych idytrych@fit.vutbr.cz
4  * File: SugBinaryAttribute.java
5  * Description: Class representing attribute of suggestion
6  */
7 
8 /**
9  * @package cz.vutbr.fit.knot.annotations.modules.suggestionManager.attributes
10  *
11  * @brief Classes for representing particular types of attributes of suggestions
12  */
13 package cz.vutbr.fit.knot.annotations.modules.suggestionManager.attributes;
14 
29 import java.io.Serializable;
30 import java.math.BigDecimal;
31 import java.util.*;
32 import java.util.logging.Level;
33 import java.util.logging.Logger;
34 import javax.persistence.*;
35 
36 /**
37  * @file SugBaseAttribute.java
38  *
39  * @brief Class representing attribute of suggestion
40  */
41 
42 /**
43  * Class representing attribute of suggestion
44  *
45  * @brief Class representing attribute of suggestion
46  */
47 @Entity(name="SuggestionAttribute")
48 @Inheritance(strategy= InheritanceType.SINGLE_TABLE)
49 @Table(name = "suggestionAttribute")
50 @DiscriminatorColumn(name="simpleType",discriminatorType=DiscriminatorType.STRING)
51 @NamedQueries({
52  @NamedQuery(name = "SuggestionAttribute.findAll", query = "SELECT a FROM SuggestionAttribute a"),
53  @NamedQuery(name = "SuggestionAttribute.findById", query = "SELECT a FROM SuggestionAttribute a WHERE a.id = :id"),
54  @NamedQuery(name = "SuggestionAttribute.findByName", query = "SELECT a FROM SuggestionAttribute a WHERE a.name = :name"),
55  @NamedQuery(name = "SuggestionAttribute.findBySimpleType", query = "SELECT a FROM SuggestionAttribute a WHERE a.simpleType = :simpleType"),
56  @NamedQuery(name = "SuggestionAttribute.findByType", query = "SELECT a FROM SuggestionAttribute a WHERE a.type = :type"),
57  @NamedQuery(name = "SuggestionAttribute.findBySuggestion", query = "SELECT a FROM SuggestionAttribute a WHERE a.suggestion = :suggestion"),
58 })
59 public abstract class SugBaseAttribute implements Serializable, Comparable<Object>, SecAttribute {
60 
61  protected static final long serialVersionUID = 1L;
62 
63 
64  /** Id of attribute of annotation */
65  @Id
66  @GeneratedValue(strategy = GenerationType.IDENTITY)
67  @Basic(optional = false)
68  @Column(name = "id")
69  protected Integer id;
70 
71 
72  /** Id of suggestion to which this attribute belongs */
73  @Basic(optional = false)
74  @Column(name = "suggestion", nullable = false, insertable = false, updatable = false)
75  protected int suggestion;
76 
77  /** Name of attribute */
78  @Basic(optional = false)
79  @Column(name = "name")
80  protected String name;
81 
82  /** Name of simple type of attribute (set in structured attributes too) */
83  @Column(name = "simpleType")
84  protected String simpleType;
85 
86  /** Id of structured type of attribute (type of annotation) */
87  @Column(name = "type", insertable = false, updatable = false)
88  protected Integer type;
89 
90  /** Value of attribute type URI or URI of linked annotation or Image */
91  @Column(name = "uri")
92  protected String uri;
93 
94  /** URI in ontology */
95  @Basic(optional = true)
96  @Column(name = "uriInOntology")
97  private String uriInOntology;
98 
99  /** Id of nested suggestion (structured value) */
100  @Column(name = "nestedSugg", insertable = false, updatable = false)
101  protected String nestedSugg;
102 
103  /** Id of nested annotation (structured value) */
104  @Column(name = "nestedAnnot", insertable = false, updatable = false)
105  protected String nestedAnnot;
106 
107  /** Id of linked suggestion (can be null in case of foreign annotation) */
108  @Column(name = "linkedSugg", insertable = false, updatable = false)
109  protected Integer linkedSugg;
110 
111  /** Id of linked annotation (can be null in case of foreign annotation) */
112  @Column(name = "linkedAnnot", insertable = false, updatable = false)
113  protected Integer linkedAnnot;
114 
115  /** Attribute value of type String */
116  @Column(name = "stringValue")
117  protected String stringValue;
118 
119  /** Attribute value of type Text */
120  @Lob
121  @Column(name = "textValue")
122  protected String textValue;
123 
124  /** Attribute value of type DateTime, Date or Time */
125  @Column(name = "dateValue")
126  @Temporal(TemporalType.TIMESTAMP)
127  protected Date dateValue;
128 
129  /** Attribute value of type integer */
130  @Column(name = "intValue")
131  protected Integer intValue;
132 
133  /** Attribute value of type Decimal */
134  @Column(name = "decValue")
135  protected BigDecimal decValue;
136 
137  /** Attribute value of type Boolean */
138  @Column(name = "boolVAlue")
139  protected Boolean boolVAlue;
140 
141  /** Attribute value of type Binary */
142  @Lob
143  @Column(name = "binValue")
144  protected byte[] binValue;
145 
146  /** Attribute value of type Person (id of user) */
147  @Column(name = "userValue", insertable = false, updatable = false)
148  protected Integer userValue;
149 
150  /** Attribute value of type GeoPoint - latitude */
151  @Column(name = "geoLat")
152  protected BigDecimal geoLat;
153  /** Attribute value of type GeoPoint - longitude */
154  @Column(name = "geoLong")
155  protected BigDecimal geoLong;
156 
157  /** type of vocalbulary entity */
158  @Column(name = "entityType")
159  protected String entityType;
160 
161  /** uri of image that entity represents */
162  @Column(name = "entityVisualURI")
163  protected String entityVisualURI;
164 
165  /** Comment */
166  @Basic(optional = true)
167  @Lob
168  @Column(name = "commentary")
169  protected String comment;
170 
171  /** Attribute priority (for order) */
172  @Column(name = "priority")
173  protected Integer priority;
174 
175  /** Annotation to which this attribute belongs */
176  @ManyToOne(optional = false)
177  @JoinColumn(name = "suggestion", referencedColumnName = "id")
178  protected Suggestion refSuggestion;
179 
180  /** Structured type of attribute (type of annotation) */
181  @OneToOne(optional = true)
182  @JoinColumn(name = "type", referencedColumnName = "id")
183  protected AnnotType attributeType;
184 
185  /** Nested suggestion (structured value) */
186  @OneToOne(optional = true, cascade = CascadeType.ALL)
187  @JoinColumn(name = "nestedSugg", referencedColumnName = "id")
188  protected Suggestion nestedSuggestion;
189 
190  /** Nested annotation (structured value) */
191  @OneToOne(optional = true, cascade = CascadeType.ALL)
192  @JoinColumn(name = "nestedAnnot", referencedColumnName = "id")
193  protected Annotation nestedAnnotation;
194 
195  /** Linked suggestion (can be null in case of foreign annotation) */
196  @OneToOne(optional = true)
197  @JoinColumn(name = "linkedSugg", referencedColumnName = "id")
198  protected Suggestion linkedSuggestion;
199 
200  /** Linked annotation (can be null in case of foreign annotation) */
201  @OneToOne(optional = true)
202  @JoinColumn(name = "linkedAnnot", referencedColumnName = "id")
203  protected Annotation linkedAnnotation;
204 
205  /** Attribute value of type Person (user) */
206  @OneToOne(optional = true)
207  @JoinColumn(name = "userValue", referencedColumnName = "id")
208  protected User user;
209 
210  /** List of entity additional attribute */
211  @OneToMany(mappedBy = "refEntityAttribute", cascade = CascadeType.ALL,
212  targetEntity = SugEntityAdditionalAttribute.class)
213  protected List<SugEntityAdditionalAttribute> entityAdditionalAttributes;
214 
215 
216  /**
217  * Constructor for base attribute. Note: every extended class MUST HAVE
218  * default constructor without parameters
219  */
220  public SugBaseAttribute() {
221  }
222 
223  /**
224  * Creates new attribute
225  * @param id id of new attribute
226  */
227  public SugBaseAttribute(Integer id) {
228  this.id = id;
229  }
230 
231 
232  /**
233  * Just a helper function,which returns XML part for comments.
234  *
235  * @param proto11 If true, protocol version is greater then 1.0
236  * @return XML part for comments
237  */
238  protected String getCommentXmlPart(boolean proto11) {
239  String sCom = "/";
240  if (proto11 && comment != null && !comment.isEmpty()) {
241  sCom = "><a:comment>"
242  + "<![CDATA["
243  + comment
244  + "]]>"
245  + "</a:comment></a:attribute";
246  }
247  return sCom;
248  }
249 
250  /**
251  * Formats attribute value for XML. Function should provide date
252  * converting for date attribute, string escaping for string based
253  * attributes...
254  *
255  * @return string representing attribute value
256  */
257  protected String xmlFormatValue() {
258  if (this.getValue() == null) {
259  return "";
260  }
261  return Util.escapeForXml(this.getValue().toString());
262  }
263 
264  /**
265  * Returns ""
266  * @return
267  */
268  public String getTypeOntologyUri(){
269  return "";
270  }
271  /**
272  * Returns serialized informations about attribute of annotation in XML
273  *
274  * @param proto11 If true, protocol version is greater then 1.0
275  * @param withOntology If true, URI in ontology will be serialized, if false, it will be omitted
276  * @return Returns serialized informations about attribute of annotation in XML
277  */
278  public String toXMLString(boolean proto11, boolean withOntology) {
279  String sCom = this.getCommentXmlPart(proto11);
280  String escapedVal = xmlFormatValue();
281  String ontoString = "";
282  if(withOntology){
283  ontoString += " typeOntologyUri=\""+getTypeOntologyUri()+"\"";
284  ontoString += " ontologyUri=\"" + getUriInOntology() + "\"";
285  }
286  return "<a:attribute name=\"" + name + "\"" + ontoString + " type=\"" + simpleType + "\" rdf:value=\"" + escapedVal + "\"" + sCom + ">";
287  }
288 
289  /**
290  * Returns serialized informations about attribute of suggestion in XML for protocol V2
291  *
292  * @return Returns serialized informations about attribute of suggestion in XML for protocol V2
293  */
294  public String toXMLStringV2(){
295  StringBuilder result = new StringBuilder();
296  String escapedVal = xmlFormatValue();
297  String ontoString = getUriInOntology();
298  String docUri = refSuggestion.getSource() + refSuggestion.getFragmentXpointersV2();
299 
300  // open triple
301  result.append("<trix:triple>");
302 
303  // make first two triples with document URI + xPointers
304  result.append("<trix:uri>");
305  result.append(docUri);
306  result.append("</trix:uri>");
307 
308  // add triple with name or name from ontology
309  if(ontoString == null || ontoString.isEmpty()){
310  // if ontology name is not presented
311  result.append("<trix:name>");
312  result.append(Util.toHTMLString(name));
313  result.append("</trix:name>");
314  }else{
315  // if ontology string is avaible
316  result.append("<trix:uri>");
317  result.append(Util.toHTMLString(ontoString));
318  result.append("</trix:uri>");
319  }
320 
321  // make attributes with special pruposes (that is geoPoint and attributes with mor then one triple)
322  if (simpleType.equals("NestedAnnotation")) {
323  result.append(((SugNestedAttribute) this).toXMLStringWHV2(ontoString));
324  } else if (simpleType.equals("AnnotationLink") || simpleType.equals("LinkedAnnotation")) {
325  // Linked annotation
326  result.append(((SugLinkedAttribute) this).toXMLStringWHV2(ontoString));
327  } else if (simpleType.equals("NestedSuggestion")) {
328  result.append(((SugNestedAttribute) this).toXMLStringWHV2(ontoString));
329  } else if (simpleType.equals("SuggestionLink") || simpleType.equals("LinkedSuggestion")) {
330  // Linked suggestion
331  result.append(((SugLinkedAttribute) this).toXMLStringWHV2(ontoString));
332  } else if (simpleType.equalsIgnoreCase("Entity")) {
333  // Entity
334  SugEntityAttribute entity = (SugEntityAttribute) this;
335  result.append(entity.toXMLStringWHV2());
336  } else if (simpleType.equalsIgnoreCase("GeoPoint")) {
337  // geo point
338  result.append("<trix:typedLiteral datatype=\"http://www.w3.org/2003/01/geo/wgs84_pos#Point\">");
339  result.append(((SugGeoPointAttribute) this).getXmlBody());
340  result.append("</trix:typedLiteral>");
341 
342  // close triple
343  result.append("</trix:triple>");
344  } else if (simpleType.equals("AnyAnnotation")) {
345  SugAnyAttribute anyAnnot = (SugAnyAttribute) this;
346  result.append(anyAnnot.toXMLStringWHV2(ontoString));
347  } else {
348  // All others simple attributes
349  int index = Constants.SIMPLE_TYPES_NAMES_V2.indexOf(simpleType);
350  if (index < 0) {
352  String msg = "Unknown type of attribute of suggestion: " + simpleType;
353  Logger.getLogger(SugBaseAttribute.class.getName()).log(Level.SEVERE, msg);
354  }
355  // use string to prevent Exception or client crash
356  index = Constants.SIMPLE_TYPES_NAMES_V2.indexOf("String");
357  }
358 
359  result.append("<trix:typedLiteral datatype=\"");
360  result.append(Constants.SIMPLE_TYPES_URI_V2.get(index));
361 
362  if(escapedVal == null){
363  // attribute is empty
364  result.append("\"/>");
365  }else{
366  result.append("\">");
367  // value with escaped XML chars
368  result.append(escapedVal);
369  result.append("</trix:typedLiteral>");
370  }
371 
372  // close triple
373  result.append("</trix:triple>");
374  }
375 
376  if (priority != null) {
377  result.append("<trix:triple>");
378  result.append("<trix:uri>");
379  result.append(docUri);
380  result.append("</trix:uri>");
381  if (ontoString == null || ontoString.isEmpty()) {
382  // if ontology name is not presented
383  result.append("<trix:name>");
384  result.append(Util.toHTMLString(name));
385  result.append("</trix:name>");
386  } else {
387  // if ontology string is avaible
388  result.append("<trix:uri>");
389  result.append(Util.toHTMLString(ontoString));
390  result.append("</trix:uri>");
391  }
392  result.append("<trix:typedLiteral datatype=\"http://knot.fit.vutbr.cz/annotations/knotOAExtension#attributePriority\">");
393  result.append(priority);
394  result.append("</trix:typedLiteral>");
395  result.append("</trix:triple>");
396  }
397 
398  return result.toString();
399  } // toXMLStringV2()
400 
401  /**
402  * Converts this attribute to SugEntityAdditionalAttribute
403  * Some conversions are not possible as a values are complex
404  *
405  * @return Returns this attribute as SugEntityAdditionalAttribute or null if conversion is not possible
406  */
409 
410  result.setName(name);
412  result.setStringValue("");
413 
414  if (simpleType.equals("NestedAnnotation")) {
415  return null;
416  } else if (simpleType.equals("AnnotationLink") || simpleType.equals("LinkedAnnotation")) {
417  return null;
418  } else if (simpleType.equals("NestedSuggestion")) {
419  return null;
420  } else if (simpleType.equals("SuggestionLink") || simpleType.equals("LinkedSuggestion")) {
421  return null;
422  } else if (simpleType.equalsIgnoreCase("Entity")) {
423  return null;
424  } else if (simpleType.equalsIgnoreCase("GeoPoint")) {
425  if (this.getGeoLat() != null && this.getGeoLong() != null) {
426  result.setStringValue(this.getGeoLat().toString() + ", " + this.getGeoLong().toString());
427  }
428  } else if (simpleType.equals("AnyAnnotation")) {
429  return null;
430  } else if (simpleType.equals("Date")) {
431  if (this.getDateValue() != null) {
432  result.setStringValue(Util.toRFC3339DateOnlyWTZ(this.getDateValue()));
433  }
434  } else if (simpleType.equals("DateTime")) {
435  if (this.getDateValue() != null) {
436  result.setStringValue(Util.toRFC3339Date(this.getDateValue()));
437  }
438  } else if (simpleType.equals("Time")) {
439  if (this.getDateValue() != null) {
440  result.setStringValue(Util.toRFC3339Time(this.getDateValue()));
441  }
442  } else if (simpleType.equals("URI")) {
443  if (this.getValue() != null) {
444  result.setStringValue(this.getValue().toString());
445  result.setType("URI");
446  }
447  } else {
448  // All others simple attributes
449  int index = Constants.SIMPLE_TYPES_NAMES_V2.indexOf(simpleType);
450  if (index < 0) {
452  String msg = "Unknown type of attribute of suggestion: " + simpleType;
453  Logger.getLogger(SugBaseAttribute.class.getName()).log(Level.SEVERE, msg);
454  }
455  return null;
456  }
457 
458  Object val = this.getValue();
459  if (val != null) {
460  result.setStringValue(val.toString());
461  }
462  }
463 
464  return result;
465  } // toSugEntityAdditionalAttribute()
466 
467  /**
468  * Compares attributes according to priority
469  *
470  * @param o Object to compare with
471  * @return Returns a negative integer, zero, or a positive integer as this
472  * object is less than, equal to, or greater than the specified object.
473  */
474  @Override
475  public int compareTo(Object o) {
476  if (o == null) {
477  return 1;
478  }
479  if (!(o instanceof SugBaseAttribute)) {
480  throw new UnsupportedOperationException("Not supported yet.");
481  }
482  SugBaseAttribute other = (SugBaseAttribute) o;
483  if (this.priority == null && other.getPriority() == null) {
484  return 0;
485  }
486  if (this.priority == null && other.getPriority() != null) {
487  return -1;
488  }
489  if (this.priority != null && other.getPriority() == null) {
490  return 1;
491  }
492  return this.priority.compareTo(other.getPriority());
493  }
494 
495 
496  /**
497  * Compares this with other object and returns, whether objects are same type
498  *
499  * and have same content (id is irrelevant). Contents of nested annotations
500  * are also compared.
501  * @param obj Object to compare with
502  * @return If object is same type and have same content, returns true, false otherwise
503  */
504  public boolean contentEquals(Object obj){
505  return contentEquals(obj,true);
506  }
507 
508  /**
509  * Compares this with other object and returns, whether objects are same type
510  *
511  * and have same content (id is irrelevant). Contents of nested annotations
512  * are also compared.
513  * @param obj Object to compare with
514  * @param refAnnot RefAnnotation will not be checked if the parameter is set to false
515  * @return If object is same type and have same content, returns true, false otherwise
516  */
517  public boolean contentEquals(Object obj, boolean refAnnot) {
518  if (obj == null) {
519  return false;
520  }
521  if (obj instanceof SugBaseAttribute) {
522  final SugBaseAttribute other = (SugBaseAttribute) obj;
523 
524  if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
525  return false;
526  }
527  if (this.userValue != other.userValue && (this.userValue == null || !this.userValue.equals(other.userValue))) {
528  return false;
529  }
530  if (this.geoLat != other.geoLat && (this.geoLat == null || !this.geoLat.equals(other.geoLat))) {
531  return false;
532  }
533  if (this.geoLong != other.geoLong && (this.geoLong == null || !this.geoLong.equals(other.geoLong))) {
534  return false;
535  }
536  if (this.attributeType != other.attributeType && (this.attributeType == null || !this.attributeType.equals(other.attributeType))) {
537  return false;
538  }
539  if (this.comment == null ? other.comment != null : !this.comment.equals(other.comment)) {
540  return false;
541  }
542  if (this.priority == null ? other.priority != null : !this.priority.equals(other.priority)) {
543  return false;
544  }
545 
546  if(refAnnot){
547  if (this.refSuggestion != other.refSuggestion && (this.refSuggestion == null || !this.refSuggestion.equals(other.refSuggestion))) {
548  return false;
549  }
550  }
551 
552  if ((this.simpleType == null) ? (other.simpleType != null) : !this.simpleType.equalsIgnoreCase(other.simpleType)) {
553  return false;
554  }
555  if(this.simpleType != null){
556  if(this.simpleType.equalsIgnoreCase("String") || this.simpleType.equalsIgnoreCase("Duration")){
557  if ((this.stringValue == null) ? (other.stringValue != null) : !this.stringValue.equals(other.stringValue)) {
558  return false;
559  }
560  }
561  else if(this.simpleType.equalsIgnoreCase("Binary")){
562  if (!Arrays.equals(this.binValue, other.binValue)) {
563  return false;
564  }
565  }
566  else if(this.simpleType.equalsIgnoreCase("Date") ||
567  this.simpleType.equalsIgnoreCase("DateTime") ||
568  this.simpleType.equalsIgnoreCase("Time") ||
569  this.simpleType.equalsIgnoreCase("Integer") ||
570  this.simpleType.equalsIgnoreCase("Decimal") ||
571  this.simpleType.equalsIgnoreCase("Boolean")){
572  if (this.getValue() != other.getValue() && (this.getValue() == null || !this.getValue().equals(other.getValue()))) {
573  return false;
574  }
575  }
576  else if(this.simpleType.equalsIgnoreCase("SuggestionLink")){
577  if (this.linkedSuggestion != other.linkedSuggestion &&
578  (this.linkedSuggestion == null || !this.linkedSuggestion.equals(other.linkedSuggestion))) {
579  return false;
580  }
581  }
582  else if(this.simpleType.equalsIgnoreCase("AnnotationLink")){
583  if (this.linkedAnnotation != other.linkedAnnotation &&
584  (this.linkedAnnotation == null || !this.linkedAnnotation.equals(other.linkedAnnotation))) {
585  return false;
586  }
587  }
588  else if(this.simpleType.equalsIgnoreCase("NestedSuggestion")){
589  if (this.nestedSuggestion != other.nestedSuggestion &&
590  (this.nestedSuggestion == null || !this.nestedSuggestion.equals(other.nestedSuggestion))) {
591  if (this.nestedSuggestion != null && other.nestedSuggestion != null) {
592  if (!this.nestedSuggestion.contentEquals(other.nestedSuggestion)) {
593  return false;
594  }
595  } else {
596  return false;
597  }
598  }
599  }
600  else if(this.simpleType.equalsIgnoreCase("Person")){
601  if (this.user != other.user && (this.user == null || !this.user.equals(other.user))) {
602  return false;
603  }
604  }
605  else if(this.simpleType.equalsIgnoreCase("GeoPoint")){
606  if (this.geoLat != other.geoLat && (this.geoLat == null || !this.geoLat.equals(other.geoLat))) {
607  return false;
608  }
609  if (this.geoLong != other.geoLong && (this.geoLong == null || !this.geoLong.equals(other.geoLong))) {
610  return false;
611  }
612  }
613  else if (this.simpleType.equalsIgnoreCase("Entity")) {
614  if (this.uri != other.uri && (this.uri == null || !this.uri.equals(other.uri))) {
615  return false;
616  }
617  if (this.entityType != other.entityType && (this.entityType == null || !this.entityType.equals(other.entityType))) {
618  return false;
619  }
620  if (this.stringValue != other.stringValue && (this.stringValue == null || !this.stringValue.equals(other.stringValue))) {
621  return false;
622  }
623  if (this.entityType != other.entityType && (this.entityType == null || !this.entityType.equals(other.entityType))) {
624  return false;
625  }
626  if (this.entityVisualURI != other.entityVisualURI && (this.entityVisualURI == null || !this.entityVisualURI.equals(other.entityVisualURI))) {
627  return false;
628  }
629  if (this.textValue != other.textValue && (this.textValue == null || !this.textValue.equals(other.textValue))) {
630  return false;
631  }
632  }
633  //AnyAnnotation, Text, URI, Image
634  else{
635  if ((this.getValue() == null) ? (other.getValue() != null) : (!this.getValue().equals(other.getValue()))) {
636  return false;
637  }
638  }
639  }
640  else{
641  if ((this.getValue() == null) ? (other.getValue() != null) : (!this.getValue().equals(other.getValue()))) {
642  return false;
643  }
644  }
645  return true;
646  }
647  return false;
648  } // contentEquals()
649 
650 
651  /**
652  * Gets value of the attribute
653  *
654  * @return value of the attribute
655  */
656  @Override
657  public abstract Object getValue();
658 
659 
660  /**
661  * Sets value of the attribute. Value should already be a native java value.
662  *
663  * For example, for attribute of type Integer value should be integer, not
664  * string. If you want to set value and parse that value, you should use
665  * setRawValue
666  *
667  * @param value new value of the attribute
668  */
669  @Override
670  public abstract void setValue(Object value);
671 
672 
673  /**
674  * Parses provided value and sets that value as a value of attribute
675  *
676  * @param value new value of the attribute in raw form from XML
677  */
678  public abstract void setRawValue(String value) throws IllegalArgumentException;
679 
680  /**
681  * Checks if attribute value is empty or default value
682  *
683  * @return true, if attribute value is empty
684  */
685  public boolean isEmpty() {
686  return this.getValue() == null;
687  }
688 
689  /**
690  * Compares this with other object and returns, whether objects are same type
691  * and have same id.
692  *
693  * @param object Object to compare with
694  * @return If object is same type and have same id, returns true, false otherwise
695  */
696  @Override
697  public boolean equals(Object object) {
698  if (!(object instanceof SugBaseAttribute)) {
699  return false;
700  }
701  SugBaseAttribute other = (SugBaseAttribute) object;
702  if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
703  return false;
704  }
705  return true;
706  }
707 
708  /**
709  * Gets textual comment
710  *
711  * @return Returns textual comment
712  */
713  public String getComment() {
714  return comment;
715  }
716 
717  /**
718  * Sets textual comment
719  *
720  * @param comment Textual comment
721  */
722  public void setComment(String comment) {
723  this.comment = comment;
724  }
725 
726  @Override
727  public String toString() {
728  return "cz.vutbr.fit.knot.annotations.entity.Attribute[id=" + id + "]";
729  }
730 
731  @Override
732  public int hashCode() {
733  int hash = 0;
734  hash += (id != null ? id.hashCode() : 0);
735  return hash;
736  }
737 
738  /**
739  * Gets name of attribute
740  *
741  * @return Returns name of attribute
742  */
743  @Override
744  public String getName() {
745  return name;
746  }
747 
748  /**
749  * Sets name of attribute
750  *
751  * @param name Name of attribute
752  */
753  @Override
754  public void setName(String name) {
755  this.name = name;
756  }
757 
758  /**
759  * Gets name of simple type of attribute
760  *
761  * @return Returns name of simple type of attribute
762  */
763  @Override
764  public String getSimpleType() {
765  return simpleType;
766  }
767 
768  /**
769  * Sets name of simple type of attribute
770  *
771  * @param simpleType Name of simple type of attribute
772  */
773  public void setSimpleType(String simpleType) {
774  this.simpleType = simpleType;
775  }
776 
777  /**
778  * Gets id of suggestion to which this attribute belongs
779  *
780  * @return Returns id of suggestion to which this attribute belongs
781  */
782  public int getSuggestion() {
783  return suggestion;
784  }
785 
786  /**
787  * Sets id of suggestion to which this attribute belongs
788  *
789  * @param suggestion id of suggestion to which this attribute belongs
790  */
791  public void setSuggestion(int suggestion) {
792  this.suggestion = suggestion;
793  }
794 
795  /**
796  * Gets annotation to which this attribute belongs
797  *
798  * @return Returns annotation to which this attribute belongs
799  */
801  return refSuggestion;
802  }
803 
804  /**
805  * Sets suggestion to which this attribute belongs
806  *
807  * @param value Suggestion to which this attribute belongs
808  */
809  public void setRefSuggestion(Suggestion value) {
810  this.refSuggestion = value;
811  }
812 
813  /**
814  * Sets suggestion to which this attribute belongs
815  *
816  * @param value Suggestion to which this attribute belongs
817  */
818  @Override
819  public void setRefSecSuggestion(SecSuggestion value) {
820  if (value instanceof Suggestion) {
821  this.refSuggestion = (Suggestion) value;
822  }
823  }
824 
825  /**
826  * Gets structured type of attribute (type of annotation)
827  *
828  * @return Returns structured type of attribute (type of annotation)
829  */
831  return attributeType;
832  }
833 
834  /**
835  * Sets structured type of attribute (type of annotation)
836  *
837  * @param attributeType Structured type of attribute (type of annotation)
838  */
839  public void setAttributeType(AnnotType attributeType) {
840  this.attributeType = attributeType;
841  }
842 
843  /**
844  * Gets nested suggestion
845  *
846  * @return Returns nested suggestion
847  */
849  return nestedSuggestion;
850  }
851 
852  /**
853  * Gets nested annotation
854  *
855  * @return Returns nested annotation
856  */
858  return nestedAnnotation;
859  }
860 
861  /**
862  * Gets tmpID of the nested suggestion
863  *
864  * @return Returns tmpID of the nested suggestion
865  */
866  public String getNestedSuggID() {
867  return nestedSugg;
868  }
869 
870  /**
871  * Sets tmpID of the nested suggestion
872  */
873  public void setNestedSuggID(String nest) {
874  if(nest != null && !nest.isEmpty()){
875  this.nestedAnnot = null;
876  }
877  this.nestedSugg = nest;
878  }
879 
880  /**
881  * Gets tmpID of the nested annotation
882  *
883  * @return Returns tmpID of the nested annotation
884  */
885  public String getNestedAnnotID() {
886  return nestedAnnot;
887  }
888 
889  /**
890  * Sets tmpID of the nested annotation
891  */
892  public void setNestedAnnotID(String nestAnnot) {
893  if(nestAnnot != null && !nestAnnot.isEmpty()){
894  this.nestedSugg = null;
895  }
896  this.nestedAnnot = nestAnnot;
897  }
898 
899  /**
900  * Sets nested suggestion
901  *
902  * @param nestedSuggestion Nested suggestion
903  */
904  public void setNestedSuggestion(Suggestion nestedSuggestion) {
905  if(nestedSuggestion != null){
906  this.nestedAnnotation = null;
907  }
908  this.nestedSuggestion = nestedSuggestion;
909  }
910 
911  /**
912  * Sets nested annotation
913  *
914  * @param nestedAnnotation Nested annotation
915  */
916  public void setNestedAnnotation(Annotation nestedAnnotation) {
917  if(nestedAnnotation != null){
918  this.nestedSuggestion = null;
919  }
920  this.nestedAnnotation = nestedAnnotation;
921  }
922 
923  /**
924  * Gets id of attribute of annotation
925  *
926  * @return returns id of attribute of annotation
927  */
928  public Integer getId() {
929  return id;
930  }
931 
932  /**
933  * Sets id of attribute of annotation
934  *
935  * @param id Id of attribute of annotation
936  */
937  public void setId(Integer id) {
938  this.id = id;
939  }
940 
941  /**
942  * Gets id of structured type of attribute (type of annotation)
943  *
944  * @return Returns id of structured type of attribute (type of annotation)
945  */
946  public Integer getType() {
947  return type;
948  }
949 
950  /**
951  * Sets id of structured type of attribute (type of annotation)
952  *
953  * @param type id of structured type of attribute (type of annotation)
954  */
955  public void setType(Integer type) {
956  this.type = type;
957  }
958 
959  /**
960  * Checks if attribute is structured. Structured attribute is for example
961  * GeoPointAttribute
962  *
963  * @return true, if attribute is structured
964  */
965  public boolean isStructured() {
966  return false;
967  }
968 
969  /**
970  * Sets attribute value of type Person (user)
971  *
972  * @param user Attribute value of type Person (user)
973  */
974  public void setUser(User user) {
975  this.user = user;
976  }
977 
978  /**
979  * Gets attribute value of type Person (user)
980  *
981  * @return Returns attribute value of type Person (user)
982  */
983  public User getUser() {
984  return user;
985  }
986 
987  /**
988  * Sets attribute value of uri
989  *
990  * @param uri Attribute value of uri
991  */
992  public void setUri(String uri){
993  this.uri = uri;
994  }
995 
996  /**
997  * Gets attribute value of uri
998  *
999  * @return Attribute value of uri
1000  */
1001  public String getUri(){
1002  return uri;
1003  }
1004 
1005  /**
1006  * Sets attributes URI in ontology
1007  *
1008  * @param uriInOntology Uri in ontology
1009  */
1010  public void setUriInOntology(String uriInOntology){
1011  this.uriInOntology = uriInOntology;
1012  }
1013 
1014  /**
1015  * Gets attributes URI in ontology
1016  *
1017  * @return URI in ontology
1018  */
1019  public String getUriInOntology(){
1020  return uriInOntology;
1021  }
1022 
1023  /**
1024  * Stores attribute data into the JSON string representation. String could be
1025  * processed on the NLP server. Method stores IDs of referenced structures, so
1026  * when creating an object from this representation, one must look up for
1027  * structures by IDs.
1028  *
1029  * @return String representation of JSON Attribute data
1030  */
1031  public String toJSONString() {
1032  StringBuilder sb = new StringBuilder("{\"attName\":\"");
1033  sb.append(this.name);
1034  sb.append("\",\"simpleType\":\"");
1035  sb.append(this.simpleType);
1036 
1037  sb.append("\",\"attValue\":\"");
1038  if (this.simpleType.equals("String")) {
1039  sb.append(this.stringValue);
1040  } else if (this.simpleType.equals("Image") || this.simpleType.equals("URI")) {
1041  sb.append(this.uri);
1042  } else if (this.simpleType.equals("Integer")) {
1043  if (this.intValue != null) {
1044  sb.append(this.intValue.toString());
1045  } else {
1046  sb.append(this.intValue);
1047  }
1048  } else if (this.simpleType.equals("Boolean")) {
1049  sb.append(this.boolVAlue.toString());
1050  } else if (this.simpleType.equals("Text")) {
1051  if (this.textValue == null) {
1052  sb.append("");
1053  }
1054  else {
1055  sb.append(this.textValue);
1056  }
1057  } else if (this.simpleType.equals("Decimal")) {
1058  sb.append(this.decValue.toString());
1059  } else if (simpleType.equals("DateTime")) {
1060  sb.append(Util.toRFC3339Date(dateValue));
1061  } else if (simpleType.equals("Date")) {
1062  sb.append(Util.toRFC3339DateOnly(dateValue));
1063  } else if (simpleType.equals("Time")) {
1064  sb.append(Util.toRFC3339Time(dateValue));
1065  } else if (this.simpleType.equals("Binary")) {
1066  if (this.binValue != null) {
1067  sb.append(org.apache.commons.codec.binary.Base64.encodeBase64String(this.binValue));
1068  }
1069  } else if (this.simpleType.equals("AnyAnnotation")) {
1070  sb.append("null");
1071  } else if (this.simpleType.equals("GeoPoint") || this.simpleType.equals("geoPoint")) {
1072  // no value has been provided; send empty attribute value
1073  if (this.geoLat == null || this.geoLong == null) {
1074  sb.append("null");
1075  }
1076  else {
1077  sb.append(this.geoLat.toString());
1078  sb.append(' ');
1079  sb.append(this.geoLong.toString());
1080  }
1081  } else if(this.simpleType.equals("entity") || this.simpleType.equals("Entity")){
1082  if(this.uri.isEmpty() || this.uri == null){
1083  sb.append("null");
1084  }else{
1085  sb.append(((EntityAttribute.Entity)getValue()).toJSONString());
1086  }
1087  }else if (this.simpleType.equals("Duration")) {
1088  sb.append(this.stringValue);
1089  }
1090  sb.append("\",\"nestedURI\":\"");
1091  if (this.nestedSuggestion != null) {
1092  sb.append(this.nestedSuggestion.getURI());
1093  this.nestedSugg = this.nestedSuggestion.getId().toString();
1094  }
1095  else {
1096  sb.append("null");
1097  }
1098  sb.append("\",\"nestedID\":\"null\",\"linkedURI\":\"");
1099  if (this.linkedSuggestion != null) {
1100  sb.append(this.linkedSuggestion.getURI());
1101  this.linkedSugg = this.linkedSuggestion.getId();
1102  }
1103  else {
1104  sb.append("null");
1105  }
1106  sb.append("\",\"linkedID\":\"null\",\"structuredType\":");
1107  if (this.linkedSuggestion != null && this.linkedSuggestion.getAnnotType() != null) {
1108  sb.append("\"").append(this.linkedSuggestion.getAnnotType().getUri().split(AppBean.getBaseTypeUri())[1].split("g[0-9]*/", 2)[1]).append("\"");
1109  }
1110  else if (this.nestedSuggestion != null && this.nestedSuggestion.getAnnotType() != null) {
1111  sb.append("\"").append(this.nestedSuggestion.getAnnotType().getUri().split(AppBean.getBaseTypeUri())[1].split("g[0-9]*/", 2)[1]).append("\"");
1112  }
1113  else {
1114  sb.append("null");
1115  }
1116  sb.append("}");
1117  return sb.toString();
1118  } // toJSONString()
1119 
1120  /**
1121  * Copies data from annotation attribute into this attribute
1122  * Linked and nested annotation and linked and nested suggestion are not copied.
1123  *
1124  * @param ba attribute of the annotation
1125  */
1127  this.name = ba.getName();
1128  this.simpleType = ba.getSimpleType();
1129  if(this.simpleType.equalsIgnoreCase("NestedAnnotation")){
1130  this.simpleType = "NestedSuggestion";
1131  }
1132  if(this.simpleType.equalsIgnoreCase("AnnotationLink")){
1133  this.simpleType = "SuggestionLink";
1134  }
1135  this.type = ba.getType();
1136  this.uri = ba.getUri();
1137  this.uriInOntology = ba.getUriInOntology();
1138  this.attributeType = ba.getAttributeType();
1139  this.comment = ba.getComment();
1140  this.entityType = ba.getEntityType();
1141  this.entityVisualURI = ba.getEntityVisualURI();
1142  this.binValue = ba.getBinValue();
1143  this.boolVAlue = ba.getBoolValue();
1144  this.dateValue = ba.getDateValue();
1145  this.decValue = ba.getDecValue();
1146  this.geoLat = ba.getGeoLat();
1147  this.geoLong = ba.getGeoLong();
1148  this.intValue = ba.getIntValue();
1149  this.stringValue = ba.getStringValue();
1150  this.textValue = ba.getTextValue();
1151  this.userValue = ba.getUserValue();
1152  this.priority = ba.getPriority();
1153  this.entityAdditionalAttributes = new ArrayList<SugEntityAdditionalAttribute>();
1154  if (ba.getEntityAdditionalAttributes() != null && this instanceof SugEntityAttribute) {
1155  Iterator<EntityAdditionalAttribute> eaIt = ba.getEntityAdditionalAttributes().iterator();
1156  while (eaIt.hasNext()) {
1157  EntityAdditionalAttribute at = eaIt.next();
1159  newAt.setPriority(at.getPriority());
1160  this.entityAdditionalAttributes.add(newAt);
1161  }
1162  }
1163 
1164  this.linkedAnnotation = null;
1165  this.nestedAnnotation = null;
1166  this.linkedSuggestion = null;
1167  this.nestedSuggestion = null;
1168  } // updateFromBaseAttribut()
1169 
1170  /**
1171  * Set this attribute from alternative attribute
1172  *
1173  * @param attr attribute of the alternative
1174  */
1176  this.name = attr.getName();
1177  this.simpleType = attr.getSimpleType();
1178  if(this.simpleType.equalsIgnoreCase("NestedAnnotation")){
1179  this.simpleType = "NestedSuggestion";
1180  }
1181  if(this.simpleType.equalsIgnoreCase("AnnotationLink")){
1182  this.simpleType = "SuggestionLink";
1183  }
1184  this.type = attr.getType();
1185  this.uri = attr.getUri();
1186  this.uriInOntology = attr.getUriInOntology();
1187  this.attributeType = attr.getAttributeType();
1188  this.comment = attr.getComment();
1189  this.entityType = attr.getEntityType();
1190  this.entityVisualURI = attr.getEntityVisualURI();
1191  this.binValue = attr.getBinValue();
1192  this.boolVAlue = attr.getBoolValue();
1193  this.dateValue = attr.getDateValue();
1194  this.decValue = attr.getDecValue();
1195  this.geoLat = attr.getGeoLat();
1196  this.geoLong = attr.getGeoLong();
1197  this.intValue = attr.getIntValue();
1198  this.stringValue = attr.getStringValue();
1199  this.textValue = attr.getTextValue();
1200  this.userValue = attr.getUserValue();
1201  this.priority = attr.getPriority();
1202 
1203  this.entityAdditionalAttributes = new ArrayList<SugEntityAdditionalAttribute>();
1204  if (attr.getEntityAdditionalAttributes() != null) {
1205  Iterator<AltEntityAdditionalAttribute> aeatit = attr.getEntityAdditionalAttributes().iterator();
1206  if (this.getClass().equals(EntityAttribute.class)) {
1207  while (aeatit.hasNext()) {
1208  AltEntityAdditionalAttribute aeat = aeatit.next();
1209  SugEntityAdditionalAttribute nAt = new SugEntityAdditionalAttribute(aeat.getName(), aeat.getType(), aeat.getStringValue(), (SugEntityAttribute) this);
1210  nAt.setPriority(aeat.getPriority());
1211  this.entityAdditionalAttributes.add(nAt);
1212  }
1213  } else if (!attr.getEntityAdditionalAttributes().isEmpty()) {
1215  Logger.getLogger(BaseAttribute.class.getName()).log(Level.SEVERE, "Trying to update attribute using attribute with different type.");
1216  }
1217  }
1218  }
1219 
1220  this.linkedAnnotation = null;
1221  this.nestedAnnotation = null;
1222  this.linkedSuggestion = null;
1223  this.nestedSuggestion = null;
1224  } // updateFromAlternativeAttribut()
1225 
1226  /**
1227  * Gets attribute value of entityType
1228  *
1229  * @return Attribute value of entityType
1230  */
1231  public String getEntityType(){
1232  return this.entityType;
1233  }
1234 
1235  /**
1236  * Sets attribute value of entityType
1237  *
1238  * @param entityType Attribute value of entityType
1239  */
1240  public void setEntityType(String entityType) {
1241  this.entityType = entityType;
1242  }
1243 
1244  /**
1245  * Gets attribute value of entityVisualURI
1246  *
1247  * @return Attribute value of entityVisualURI
1248  */
1249  public String getEntityVisualURI(){
1250  return this.entityVisualURI;
1251  }
1252 
1253  /**
1254  * Sets attribute value of entityVisualURI
1255  *
1256  * @param entityVisualURI New value of entityVisualURI
1257  */
1258  public void setEntityVisualURI(String entityVisualURI) {
1259  this.entityVisualURI = entityVisualURI;
1260  }
1261 
1262  /**
1263  * Gets attribute value of boolean
1264  *
1265  * @return Attribute value of boolean
1266  */
1267  public Boolean getBoolValue(){
1268  return this.boolVAlue;
1269  }
1270 
1271  /**
1272  * Gets attribute value of binary
1273  *
1274  * @return Attribute value of binary
1275  */
1276  public byte[] getBinValue(){
1277  return this.binValue;
1278  }
1279 
1280  /**
1281  * Gets attribute value of date
1282  *
1283  * @return Attribute value of date
1284  */
1285  public Date getDateValue(){
1286  return this.dateValue;
1287  }
1288 
1289  /**
1290  * Gets attribute value of decimal
1291  *
1292  * @return Attribute value of decimal
1293  */
1294  @Override
1295  public BigDecimal getDecValue(){
1296  return this.decValue;
1297  }
1298 
1299  /**
1300  * Gets attribute value of geo latitude
1301  *
1302  * @return Attribute value of geo latitude
1303  */
1304  public BigDecimal getGeoLat(){
1305  return this.geoLat;
1306  }
1307 
1308  /**
1309  * Gets attribute value of geo longitude
1310  *
1311  * @return Attribute value of geo longitude
1312  */
1313  public BigDecimal getGeoLong(){
1314  return this.geoLong;
1315  }
1316 
1317  /**
1318  * Gets attribute value of integer
1319  *
1320  * @return Attribute value of integer
1321  */
1322  public Integer getIntValue(){
1323  return this.intValue;
1324  }
1325 
1326  /**
1327  * Gets attribute value of string
1328  *
1329  * @return Attribute value of string
1330  */
1331  @Override
1332  public String getStringValue(){
1333  return this.stringValue;
1334  }
1335 
1336  /**
1337  * Gets attribute value of text
1338  *
1339  * @return Attribute value of text
1340  */
1341  @Override
1342  public String getTextValue(){
1343  return this.textValue;
1344  }
1345 
1346 
1347  /**
1348  * Gets attribute value of user
1349  *
1350  * @return Attribute value of user
1351  */
1352  public Integer getUserValue(){
1353  return this.userValue;
1354  }
1355 
1356  /**
1357  * Gets attribute value of linked annotation
1358  *
1359  * @return Attribute value of user
1360  */
1362  return this.linkedAnnotation;
1363  }
1364 
1365  /**
1366  * Gets attribute value of linked suggestion
1367  *
1368  * @return Attribute value of user
1369  */
1371  return this.linkedSuggestion;
1372  }
1373 
1374  /**
1375  * Sets attributes linked suggestion
1376  * @param suggestion Linked suggestion
1377  */
1378  public void setLinkedSuggestion(Suggestion suggestion){
1379  this.linkedSuggestion = suggestion;
1380  }
1381 
1382  /**
1383  * Gets attribute priority
1384  *
1385  * @return Returns attribute priority
1386  */
1387  @Override
1388  public Integer getPriority() {
1389  return priority;
1390  }
1391 
1392  /**
1393  * Sets attribute priority
1394  *
1395  * @param priority Attribute priority
1396  */
1397  @Override
1398  public void setPriority(Integer priority) {
1399  this.priority = priority;
1400  }
1401 
1402  /**
1403  * Gets list of entity additional attribute
1404  *
1405  * @return List of entity additional attribute
1406  */
1407  public List<SugEntityAdditionalAttribute> getEntityAdditionalAttributes() {
1408  return entityAdditionalAttributes;
1409  }
1410 
1411  /**
1412  * Sets list of entity additional attribute
1413  *
1414  * @param entityAdditionalAttributes List of entity additional attribute
1415  */
1416  public void setEntityAdditionalAttributes(List<SugEntityAdditionalAttribute> entityAdditionalAttributes) {
1417  this.entityAdditionalAttributes = entityAdditionalAttributes;
1418  }
1419 
1420  /**
1421  * Copies Attribute class attributes to a new instance
1422  *
1423  * @param ba attribute of the annotation
1424  */
1426  this.attributeType = ba.getAttributeType();
1427  this.binValue = ba.getBinValue();
1428  this.boolVAlue = ba.getBoolValue();
1429  this.comment = ba.getComment();
1430  this.dateValue = ba.getDateValue();
1431  this.decValue = ba.getDecValue();
1432  this.entityType = ba.getEntityType();
1433  this.entityVisualURI = ba.getEntityVisualURI();
1434  this.geoLat = ba.getGeoLat();
1435  this.geoLong = ba.getGeoLong();
1436  this.id = ba.getId();
1437  this.intValue = ba.getIntValue();
1438  this.linkedAnnot = ba.linkedAnnot;
1439  this.linkedAnnotation = ba.getLinkedAnnotation();
1440  this.linkedSugg = ba.linkedSugg;
1441  this.nestedSuggestion = ba.getNestedSuggestion();
1442  this.name = ba.getName();
1443  this.nestedAnnot = ba.nestedAnnot;
1444  this.nestedAnnotation = ba.getNestedAnnotation();
1445  this.nestedSugg = ba.nestedSugg;
1446  this.linkedSuggestion = ba.getLinkedSuggestion();
1447  this.refSuggestion = ba.refSuggestion;
1448  this.stringValue = ba.getStringValue();
1449  this.suggestion = ba.suggestion;
1450  this.textValue = ba.getTextValue();
1451  this.type = ba.getType();
1452  this.uri = ba.getUri();
1453  this.uriInOntology = ba.getUriInOntology();
1454  this.user = ba.user;
1455  this.userValue = ba.getUserValue();
1456  this.priority = ba.getPriority();
1457  }
1458 
1459  /**
1460  * Sets boolean value of attribute
1461  *
1462  * @param boolVAlue New boolean value of attribute
1463  */
1464  public void setBoolVAlue(Boolean boolVAlue) {
1465  this.boolVAlue = boolVAlue;
1466  }
1467 
1468 } // public abstract class SugBaseAttribute
1469 
List< EntityAdditionalAttribute > getEntityAdditionalAttributes()
Class representing attribute of type NestedAnnotation for peupose of suggestion.
Singleton for storing global variables.
Definition: AppBean.java:47
Class representing vocabulary entity attribute for prupose of suggestion.
Class representing vocabulary entity attribute.
Interface for SugBaseAttribute and AlternativeAttribuge.
Base class representing attribute of annotation.
Class representing type of annotation.
Definition: AnnotType.java:58
Class representing user.
Definition: User.java:51
static final String DEFAULT_SIMPLE_TYPE_FOR_ENT_AD_AT
Definition: Constants.java:225
Class representing additional attribute of entity in alternative attribute.
Class representing attribute of type AnnotationLink for prupose of suggestion.
void setEntityAdditionalAttributes(List< SugEntityAdditionalAttribute > entityAdditionalAttributes)
Class representing suggestion of annotation.
Definition: Suggestion.java:87
Class representing attribute of type AnyAnnotation for prupose of suggestion.
Utility class (manipulates RFC 3339 dates)
Definition: Util.java:29