001/**
002 * Copyright 2014 Tampere University of Technology, Pori Department
003 * 
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * 
008 *   http://www.apache.org/licenses/LICENSE-2.0
009 * 
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package service.tut.pori.contentanalysis;
017
018import java.util.Date;
019import java.util.EnumSet;
020import java.util.List;
021
022import javax.xml.bind.annotation.XmlAccessType;
023import javax.xml.bind.annotation.XmlAccessorType;
024import javax.xml.bind.annotation.XmlElement;
025import javax.xml.bind.annotation.XmlEnum;
026import javax.xml.bind.annotation.XmlEnumValue;
027import javax.xml.bind.annotation.XmlRootElement;
028
029import org.apache.commons.lang3.StringUtils;
030import org.apache.log4j.Logger;
031import org.apache.solr.client.solrj.beans.Field;
032
033import service.tut.pori.contentanalysis.CAContentCore.ServiceType;
034import service.tut.pori.contentanalysis.CAContentCore.Visibility;
035import service.tut.pori.contentanalysis.video.TimecodeList;
036import core.tut.pori.dao.SolrDAO;
037import core.tut.pori.http.parameters.DataGroups;
038import core.tut.pori.users.UserIdentity;
039import core.tut.pori.utils.MediaUrlValidator.MediaType;
040
041/**
042 * <p>A class that defines a single media object. All media objects are by default of type {@link core.tut.pori.utils.MediaUrlValidator.MediaType#UNKNOWN}}.</p>
043 * 
044 * <p>Note: when using this class, never pass partial objects to database methods, missing information 
045 * (e.g. value == null) may be assumed to be marked as "deleted", and will be removed from the database.</p>
046 * 
047 * <p>Media objects are objects which were identified from the content given for analysis (e.g. from photos).</p>
048 * 
049 * <p>Back-ends may only edit objects which have been created by them (checked by objectId, {@link #getObjectId()} and backendId, {@link #getBackendId()}). Back-ends may not edit objects which have been confirmed by the user.</p>
050 * 
051 * <p>If a back-end wants to modify an object created by another back-end, it must generate and submit a new ({@link service.tut.pori.contentanalysis.MediaObject.ConfirmationStatus#CANDIDATE}) media object.</p>
052 * 
053 * <h2>Optional Elements</h2>
054 * 
055 * <ul>
056 *  <li>{@value service.tut.pori.contentanalysis.Definitions#ELEMENT_BACKEND_ID}, missing if the object is not generated by a back-end.</li>
057 *  <li>{@value service.tut.pori.contentanalysis.Definitions#ELEMENT_VALUE}</li>
058 *  <li>{@value service.tut.pori.contentanalysis.Definitions#ELEMENT_NAME}</li>
059 *  <li>{@value service.tut.pori.contentanalysis.Definitions#ELEMENT_CONFIDENCE}, missing value means that the confidence is unknown.</li>
060 *  <li>{@value service.tut.pori.contentanalysis.Definitions#ELEMENT_OBJECT_ID}, may not be present on objects created by the system.</li>
061 *  <li>{@value service.tut.pori.contentanalysis.Definitions#ELEMENT_MEDIA_TYPE}, if omitted it will default to {@link core.tut.pori.utils.MediaUrlValidator.MediaType#UNKNOWN} OR if object is part of a container object, such as {@link service.tut.pori.contentanalysis.Media} the container's type will be used (see {@link service.tut.pori.contentanalysis.Media#getMediaType()}).</li>
062 *  <li>{@value service.tut.pori.contentanalysis.Definitions#ELEMENT_MEDIA_OBJECT_ID}, can be omitted (and is ignored) for new objects. Automatically generated by the system.</li>
063 *  <li>{@value service.tut.pori.contentanalysis.Definitions#ELEMENT_RANK}, if missing, the value should be assumed to be 0 (neutral).</li>
064 *  <li>{@value service.tut.pori.contentanalysis.Definitions#ELEMENT_RESULT_INFO}</li>
065 *  <li>{@value service.tut.pori.contentanalysis.Definitions#ELEMENT_SERVICE_ID}, not present for objects created by user or a back-end.</li>
066 *  <li>{@value service.tut.pori.contentanalysis.video.Definitions#ELEMENT_TIMECODELIST}. If timecode list is missing or is empty, the object is assumed to describe the entire content.</li>
067 *  <li>{@value service.tut.pori.contentanalysis.Definitions#ELEMENT_VISUAL_SHAPE}</li>
068 *  <li>{@value service.tut.pori.contentanalysis.Definitions#ELEMENT_VISIBILITY}, if missing should be assumed to be {@link service.tut.pori.contentanalysis.CAContentCore.Visibility#PRIVATE}.</li>
069 * </ul>
070 * 
071 * <h3>XML Example</h3>
072 * 
073 * {@doc.restlet service="[service.tut.pori.contentanalysis.reference.Definitions#SERVICE_CA_REFERENCE_EXAMPLE]" method="[service.tut.pori.contentanalysis.Definitions#ELEMENT_MEDIA_OBJECT]" type="GET" query="" body_uri=""}
074 *
075 * @see service.tut.pori.contentanalysis.VisualShape
076 */
077@XmlRootElement(name=Definitions.ELEMENT_MEDIA_OBJECT)
078@XmlAccessorType(XmlAccessType.NONE)
079public class MediaObject implements VisualShape.VisualShapeSolrCapable {
080  private static final String CONFIRMATIONSTATUS_BACKEND_REMOVED = "BACKEND_REMOVED";
081  private static final String CONFIRMATIONSTATUS_CANDIDATE = "CANDIDATE";
082  private static final String CONFIRMATIONSTATUS_NO_FRIENDLY_KEYWORD = "NO_FRIENDLY_KEYWORD";
083  private static final String CONFIRMATIONSTATUS_USER_CONFIRMED = "USER_CONFIRMED";
084  private static final String CONFIRMATIONSTATUS_USER_REJECTED = "USER_REJECTED";
085  private static final String MEDIAOBJECTTYPE_FACE = "FACE";
086  private static final String MEDIAOBJECTTYPE_KEYWORD = "KEYWORD";
087  private static final String MEDIAOBJECTTYPE_METADATA = "METADATA";
088  private static final String MEDIAOBJECTTYPE_OBJECT = "OBJECT";
089  private static final Logger LOGGER = Logger.getLogger(MediaObject.class);
090  @Field(Definitions.SOLR_FIELD_BACKEND_ID)
091  @XmlElement(name = Definitions.ELEMENT_BACKEND_ID)
092  private Integer _backendId = null;
093  @Field(Definitions.SOLR_FIELD_CONFIDENCE)
094  @XmlElement(name = Definitions.ELEMENT_CONFIDENCE)
095  private Double _confidence = null;
096  /** Unique, internally generated id */
097  @Field(SolrDAO.SOLR_FIELD_ID)
098  @XmlElement(name = Definitions.ELEMENT_MEDIA_OBJECT_ID)
099  private String _mediaObjectId = null;
100  @XmlElement(name = Definitions.ELEMENT_MEDIA_TYPE)
101  private MediaType _mediaType = MediaType.UNKNOWN;
102  @Field(Definitions.SOLR_FIELD_NAME)
103  @XmlElement(name = Definitions.ELEMENT_NAME)
104  private String _name = null;
105  /** Externally generated id. Unique when used in combination with back-end id, or if back-end id is missing, with user id. */
106  @Field(Definitions.SOLR_FIELD_CREATOR_OBJECT_ID)
107  @XmlElement(name = Definitions.ELEMENT_OBJECT_ID)
108  private String _objectId = null;
109  @Field(Definitions.SOLR_FIELD_RANK)
110  @XmlElement(name = Definitions.ELEMENT_RANK)
111  private Integer _rank = null;
112  @XmlElement(name = Definitions.ELEMENT_SERVICE_ID)
113  private ServiceType _serviceType = null;
114  @XmlElement(name = Definitions.ELEMENT_VISUAL_SHAPE)
115  private VisualShape _shape = null;
116  @XmlElement(name = Definitions.ELEMENT_STATUS)
117  private ConfirmationStatus _status = null;
118  @XmlElement(name = Definitions.ELEMENT_MEDIA_OBJECT_TYPE)
119  private MediaObjectType _type = null;
120  @Field(Definitions.SOLR_FIELD_UPDATED)
121  private Date _updated = null;
122  private UserIdentity _userId = null;
123  @Field(Definitions.SOLR_FIELD_VALUE)
124  @XmlElement(name = Definitions.ELEMENT_VALUE)
125  private String _value = null;
126  @XmlElement(name = Definitions.ELEMENT_VISIBILITY)
127  private Visibility _visibility = null;
128  @XmlElement(name = service.tut.pori.contentanalysis.video.Definitions.ELEMENT_TIMECODELIST)
129  private TimecodeList _timecodes = null;
130
131  /**
132   * The type of the media object. 
133   * 
134   */
135  @XmlEnum
136  public enum MediaObjectType{
137    /** media object containing a keyword or a tag */
138    @XmlEnumValue(value = MEDIAOBJECTTYPE_KEYWORD)
139    KEYWORD(1),
140    /** media object containing generic name-value type metadata */
141    @XmlEnumValue(value = MEDIAOBJECTTYPE_METADATA)
142    METADATA(2),
143    /** generic unspecified media object */
144    @XmlEnumValue(value = MEDIAOBJECTTYPE_OBJECT)
145    OBJECT(3),
146    /** media object containing a face recognition data */
147    @XmlEnumValue(value = MEDIAOBJECTTYPE_FACE)
148    FACE(4);
149
150    private int _value;
151
152    /**
153     * 
154     * @param value
155     */
156    private MediaObjectType(int value){
157      _value = value;
158    }
159
160    /**
161     * 
162     * @return the type as integer
163     */
164    public int toInt(){
165      return _value;
166    }
167
168    /**
169     * 
170     * @param types
171     * @return the set of types converted to int array or null if null or empty set was passed
172     */
173    public static int[] toIntArray(EnumSet<MediaObjectType> types){
174      if(types == null || types.isEmpty()){
175        LOGGER.debug("Empty set.");
176        return null;
177      }
178      int[] array = new int[types.size()];
179      int index = 0;
180      for(MediaObjectType t : types){
181        array[index++] = t.toInt();
182      }
183      return array;
184    }
185
186    /**
187     * 
188     * @param types
189     * @return data groups associated with the given types or null if null or empty set was passed
190     */
191    public static DataGroups getDataGroup(EnumSet<MediaObjectType> types){
192      if(types == null || types.isEmpty())
193        return null;
194      DataGroups groups = new DataGroups();
195      for(MediaObjectType type : types){
196        switch(type){
197          case FACE:
198            groups.addDataGroup(Definitions.DATA_GROUP_FACE);
199            break;
200          case KEYWORD:
201            groups.addDataGroup(Definitions.DATA_GROUP_KEYWORDS);
202            break;
203          case METADATA:
204            groups.addDataGroup(Definitions.DATA_GROUP_METADATA);
205            break;
206          case OBJECT:
207            groups.addDataGroup(Definitions.DATA_GROUP_OBJECT);
208            break;
209          default:
210            LOGGER.warn("Ignored unknown object type: "+type.toInt());
211        }  // switch
212      }  // for
213      return groups;
214    }
215
216    /**
217     * 
218     * @param value
219     * @return value converted to a type
220     * @throws IllegalArgumentException on bad value
221     */
222    public static MediaObjectType fromInt(int value) throws IllegalArgumentException{
223      for(MediaObjectType t : MediaObjectType.values()){
224        if(t._value == value){
225          return t;
226        }
227      }
228      throw new IllegalArgumentException("Bad "+MediaObjectType.class.toString()+" : "+value);
229    }
230
231    /**
232     * Allows convertion of data group string to enumerations based on the enumeration value (string/name)
233     * 
234     * @param dataGroups
235     * @return set of types associated with the given data groups or null if empty or null data group was passed
236     */
237    public static EnumSet<MediaObjectType> fromDataGroups(DataGroups dataGroups){
238      if(DataGroups.isEmpty(dataGroups)){
239        LOGGER.debug("Empty data group.");
240        return null;
241      }
242
243      if(DataGroups.hasDataGroup(DataGroups.DATA_GROUP_ALL, dataGroups)){
244        return EnumSet.allOf(MediaObjectType.class);
245      }
246
247      EnumSet<MediaObjectType> results = EnumSet.noneOf(MediaObjectType.class);
248      if(DataGroups.hasDataGroup(Definitions.DATA_GROUP_KEYWORDS, dataGroups)){
249        results.add(KEYWORD);
250      }
251      if(DataGroups.hasDataGroup(Definitions.DATA_GROUP_FACE, dataGroups)){
252        results.add(FACE);
253      }
254      if(DataGroups.hasDataGroup(Definitions.DATA_GROUP_METADATA, dataGroups)){
255        results.add(METADATA);
256      }
257      if(DataGroups.hasDataGroup(Definitions.DATA_GROUP_OBJECT, dataGroups)){
258        results.add(OBJECT);
259      }
260
261      if(results.isEmpty()){
262        return null;
263      }else{
264        return results;
265      }
266    }
267  }  // enum MediaObjectType
268
269  /**
270   * The confirmation status of the media object.
271   *
272   * 
273   */
274  @XmlEnum
275  public enum ConfirmationStatus{
276    /** object has been automatically generated and user has not confirmed whether he/she wants it or not */
277    @XmlEnumValue(value = CONFIRMATIONSTATUS_CANDIDATE)
278    CANDIDATE(1),
279    /** this object has been accepted by the user, it can originally be created by a back-end or by the user */
280    @XmlEnumValue(value = CONFIRMATIONSTATUS_USER_CONFIRMED)
281    USER_CONFIRMED(2),
282    /** basically this means that user does not want the object, and it should be deleted. */
283    @XmlEnumValue(value = CONFIRMATIONSTATUS_USER_REJECTED)
284    USER_REJECTED(3),
285    /** back-end has requested this object to be removed as not being valid, possibly because of previously inaccurate analysis results. */
286    @XmlEnumValue(value = CONFIRMATIONSTATUS_BACKEND_REMOVED)
287    BACKEND_REMOVED(4),
288    /** special status declaring that no friendly keyword data is available */
289    @XmlEnumValue(value = CONFIRMATIONSTATUS_NO_FRIENDLY_KEYWORD)
290    NO_FRIENDLY_KEYWORD(5);
291
292    private int _value;
293
294    /**
295     * 
296     * @param value
297     */
298    private ConfirmationStatus(int value){
299      _value = value;
300    }
301
302    /**
303     * 
304     * @return status as integer
305     */
306    public int toInt(){
307      return _value;
308    }
309
310    /**
311     * 
312     * @param status
313     * @return the given set as an integer array or null if null or empty set was passed
314     */
315    public static int[] toIntArray(EnumSet<ConfirmationStatus> status){
316      if(status == null || status.isEmpty()){
317        return null;
318      }
319      int[] array = new int[status.size()];
320      int index = 0;
321      for(ConfirmationStatus c : status){
322        array[index++] = c.toInt();
323      }
324      return array;
325    }
326
327    /**
328     * 
329     * @param value
330     * @return the value converted to status
331     * @throws IllegalArgumentException
332     */
333    public static ConfirmationStatus fromInt(int value) throws IllegalArgumentException{
334      for(ConfirmationStatus t : ConfirmationStatus.values()){
335        if(t._value == value){
336          return t;
337        }
338      }
339      throw new IllegalArgumentException("Bad "+ConfirmationStatus.class.toString()+" : "+value);
340    }
341
342    /**
343     * 
344     * @param dataGroups
345     * @return null if datagroups is null or empty or does not contain any valid datagroups
346     * 
347     */
348    public static EnumSet<ConfirmationStatus> fromDataGroups(DataGroups dataGroups){
349      if(dataGroups == null){
350        return null;
351      }
352      
353      if(DataGroups.hasDataGroup(DataGroups.DATA_GROUP_ALL, dataGroups)){
354        return EnumSet.allOf(ConfirmationStatus.class);
355      }
356      
357      EnumSet<ConfirmationStatus> results = EnumSet.noneOf(ConfirmationStatus.class);
358      if(DataGroups.hasDataGroup(Definitions.DATA_GROUP_BACKEND_REMOVED, dataGroups)){
359        results.add(BACKEND_REMOVED);
360      }   
361      if(DataGroups.hasDataGroup(Definitions.DATA_GROUP_CANDIDATE, dataGroups)){
362        results.add(CANDIDATE);
363      }   
364      if(DataGroups.hasDataGroup(Definitions.DATA_GROUP_USER_CONFIRMED, dataGroups)){
365        results.add(USER_CONFIRMED);
366      } 
367      if(DataGroups.hasDataGroup(Definitions.DATA_GROUP_USER_REJECTED, dataGroups)){
368        results.add(USER_REJECTED);
369      }
370      
371      if(results.isEmpty()){
372        return null;
373      }else{
374        return results;
375      }
376    }
377  }  // enum ConfirmationStatus
378
379  /**
380   * for serialization, must be public for solr.
381   */
382  public MediaObject(){
383    // nothing needed
384  }
385
386  /**
387   * 
388   * @param mediaType
389   * @param mediaObjectType
390   */
391  public MediaObject(MediaType mediaType, MediaObjectType mediaObjectType){
392    _type = mediaObjectType;
393    _mediaType = mediaType;
394  }
395
396  /**
397   * 
398   * @return confidence estimate for this object, if &lt; 1 or null, the confidence is assumed to be unknown.
399   * @see #setConfidence(Double)
400   */
401  public Double getConfidence() {
402    return _confidence;
403  }
404
405  /**
406   * 
407   * @param confidence
408   * @see #getConfidence()
409   */
410  public void setConfidence(Double confidence) {
411    _confidence = confidence;
412  }
413
414  /**
415   * 
416   * @return the owner user identity
417   * @see #setOwnerUserId(UserIdentity)
418   */
419  public UserIdentity getOwnerUserId() {
420    return _userId;
421  }
422
423  /**
424   * 
425   * @param userId
426   * @see #getOwnerUserId()
427   */
428  public void setOwnerUserId(UserIdentity userId) {
429    _userId = userId;
430  }
431  
432  /**
433   * @see #getOwnerUserId()
434   * 
435   * @return owner user identity value
436   */
437  @XmlElement(name = core.tut.pori.users.Definitions.ELEMENT_USER_ID)
438  public Long getOwnerUserIdValue() {
439    return (_userId == null ? null : _userId.getUserId());
440  }
441
442  /**
443   * for serialization
444   * @param userId
445   * @see #setOwnerUserId(UserIdentity)
446   */
447  @Field(Definitions.SOLR_FIELD_USER_ID)
448  private void setOwnerUserIdValue(Long userId) {
449    _userId = (userId == null ? null : new UserIdentity(userId));
450  }
451
452  /**
453   * 
454   * @return service type
455   * @see #setServiceType(service.tut.pori.contentanalysis.CAContentCore.ServiceType)
456   */
457  public ServiceType getServiceType() {
458    return _serviceType;
459  }
460
461  /**
462   * 
463   * @param serviceType
464   * @see #getServiceType()
465   */
466  public void setServiceType(ServiceType serviceType) {
467    _serviceType = serviceType;
468  }
469
470  /**
471   * 
472   * @return object value, can be same as name
473   * @see #getName()
474   * @see #setValue(String)
475   */
476  public String getValue() {
477    return _value;
478  }
479
480  /**
481   * Generally externally generated id, unique only when used in combination with back-end id, or if back-end id is missing, with user id.
482   * 
483   * @return non-unique creator specific object id
484   * @see #setObjectId(String)
485   */
486  public String getObjectId() {
487    return _objectId;
488  }
489
490  /**
491   * 
492   * @param objectId
493   * @see #getObjectId()
494   */
495  public void setObjectId(String objectId) {
496    _objectId = objectId;
497  }
498
499  /**
500   * Internally generated, unique Id.
501   * 
502   * @return DB (row) id
503   * @see #setMediaObjectId(String)
504   */
505  public String getMediaObjectId() {
506    return _mediaObjectId;
507  }
508
509  /**
510   * 
511   * @param id DB (row) id
512   * @see #getMediaObjectId()
513   */
514  public void setMediaObjectId(String id) {
515    _mediaObjectId = id;
516  }
517
518  /**
519   * 
520   * @return back-end id
521   * @see #setBackendId(Integer)
522   */
523  public Integer getBackendId() {
524    return _backendId;
525  }
526
527  /**
528   * 
529   * @param backendId
530   * @see #getBackendId()
531   */
532  public void setBackendId(Integer backendId) {
533    _backendId = backendId;
534  }
535
536  /**
537   * 
538   * @return status
539   * @see #setConfirmationStatus(ConfirmationStatus)
540   */
541  public ConfirmationStatus getConfirmationStatus() {
542    return _status;
543  }
544
545  /**
546   * 
547   * @param status
548   * @see #getConfirmationStatus()
549   */
550  public void setConfirmationStatus(ConfirmationStatus status) {
551    _status = status;
552  }
553  
554  /**
555   * for serialization
556   * 
557   * @param value
558   * @see #setConfirmationStatus(ConfirmationStatus)
559   */
560  @Field(Definitions.SOLR_FIELD_STATUS)
561  private void setConfirmationStatusValue(Integer value){
562    _status = (value == null ? null : ConfirmationStatus.fromInt(value));
563  }
564  
565  /**
566   * @see #getConfirmationStatus()
567   * 
568   * @return status value
569   */
570  public Integer getConfirmationStatusValue(){
571    return (_status == null ? null : _status.toInt());
572  }
573
574  /**
575   * 
576   * @return shape
577   * @see #setVisualShape(VisualShape)
578   */
579  public VisualShape getVisualShape() {
580    return _shape;
581  }
582
583  /**
584   * 
585   * @param shape
586   * @see #getVisualShape()
587   */
588  public void setVisualShape(VisualShape shape) {
589    _shape = shape;
590  }
591
592  /**
593   * 
594   * @param value
595   * @see #getValue()
596   */
597  public void setValue(String value) {
598    _value = value;
599  }
600
601  /**
602   * 
603   * @param type
604   * @see #getMediaObjectType()
605   */
606  public void setMediaObjectType(MediaObjectType type) {
607    _type = type;
608  }
609  
610  /**
611   * 
612   * @return object type
613   * @see #setMediaObjectType(MediaObjectType)
614   */
615  public MediaObjectType getMediaObjectType() {
616    return _type;
617  }
618  
619  /**
620   * 
621   * @param value
622   * @see #setMediaObjectType(MediaObjectType)
623   */
624  @Field(Definitions.SOLR_FIELD_MEDIA_OBJECT_TYPE)
625  private void setMediaObjectTypeValue(Integer value) {
626    _type = (value == null ? null : MediaObjectType.fromInt(value));
627  }
628  
629  /**
630   * @see #getMediaObjectType()
631   * 
632   * @return object type value
633   */
634  public Integer getMediaObjectTypeValue() {
635    return (_type == null ? null : _type.toInt());
636  }
637  
638  /**
639   * 
640   * @param serviceId
641   * @see #setServiceType(service.tut.pori.contentanalysis.CAContentCore.ServiceType)
642   */
643  @Field(Definitions.SOLR_FIELD_SERVICE_ID)
644  private void setServiceId(Integer serviceId){
645    _serviceType = (serviceId == null ? null : ServiceType.fromServiceId(serviceId));
646  }
647  
648  /**
649   * @see #getServiceType()
650   * 
651   * @return service type id
652   */
653  public Integer getServiceId() {
654    return (_serviceType == null ? null : _serviceType.getServiceId());
655  }
656
657  /**
658   * 
659   * @return name or short description of the object
660   * @see #setName(String)
661   */
662  public String getName() {
663    return _name;
664  } 
665
666  /**
667   * 
668   * @param name
669   * @see #getName()
670   */
671  public void setName(String name) {
672    _name = name;
673  }
674
675  /**
676   * 
677   * @param object can be null
678   * @return true if the passed object is valid
679   */
680  public static boolean isValid(MediaObject object){
681    if(object == null){
682      return false;
683    }else{
684      return object.isValid();
685    }
686  }
687
688  /**
689   * 
690   * @return true if the object is valid
691   * @see #isValid(MediaObject)
692   */
693  protected boolean isValid(){
694    if(_objectId == null || _status == null || _type == null){
695      if(_mediaObjectId == null){
696        LOGGER.debug("No mediaObjectId.");
697      }
698      return false;
699    }else if(MediaObjectType.KEYWORD.equals(_type) && StringUtils.isBlank(_value) && StringUtils.isBlank(_value)){
700      LOGGER.warn("Invalid name or value for object of type "+MediaObjectType.KEYWORD.name());
701      return false;
702    }else if(MediaObjectType.METADATA.equals(_type) && (StringUtils.isBlank(_value) || StringUtils.isBlank(_value))){
703      LOGGER.warn("Invalid name/value pair for object of type "+MediaObjectType.METADATA.name());
704      return false;
705    }else if(_timecodes != null && !TimecodeList.isValid(_timecodes)){
706      LOGGER.warn("Invalid timecode list.");
707      return false;
708    }else if(_shape != null){
709      return VisualShape.isValid(_shape);
710    }else{
711      return true;
712    }
713  }
714  
715  /**
716   * 
717   * @param visibility 
718   * @see #getVisibility()
719   */
720  public void setVisibility(Visibility visibility){
721    _visibility = visibility;
722  }
723
724  /**
725   * 
726   * @return object visibility
727   * @see #setVisibility(service.tut.pori.contentanalysis.CAContentCore.Visibility)
728   */
729  public Visibility getVisibility(){
730    return _visibility;
731  }
732  
733  /**
734   * @see #getVisibility()
735   * 
736   * @return visibility value
737   */
738  public Integer getVisibilityValue(){
739    return (_visibility == null ? null : _visibility.toInt());
740  }
741  
742  /**
743   * 
744   * @param visibility
745   * @see #setVisibility(service.tut.pori.contentanalysis.CAContentCore.Visibility)
746   */
747  @Field(Definitions.SOLR_FIELD_VISIBILITY)
748  private void setVisibilityValue(Integer visibility){
749    _visibility = (visibility == null ? null : Visibility.fromInt(visibility));
750  }
751
752  /**
753   * @return the updated
754   */
755  public Date getUpdated() {
756    return _updated;
757  }
758
759  /**
760   * @param updated the updated to set
761   */
762  public void setUpdated(Date updated) {
763    _updated = updated;
764  }
765
766  @Field(Definitions.SOLR_FIELD_VISUAL_SHAPE_TYPE)
767  @Override
768  public void setVisualShapeTypeId(Integer type) throws IllegalArgumentException {
769    _shape = VisualShape.setVisualShapeTypeId(_shape, type);
770  }
771
772  @Override
773  public Integer getVisualShapeTypeId() {
774    return VisualShape.getVisualShapeTypeId(_shape);
775  }
776
777  @Field(Definitions.SOLR_FIELD_VISUAL_SHAPE_VALUE)
778  @Override
779  public void setVisualShapeValue(String value) {
780    _shape = VisualShape.setValue(_shape, value);
781  }
782
783  @Override
784  public String getVisualShapeValue() {
785    return VisualShape.getValue(_shape);
786  }
787
788  /**
789   * @return the rank
790   * @see #setRank(Integer)
791   */
792  public Integer getRank() {
793    return _rank;
794  }
795
796  /**
797   * @param rank the rank to set
798   * @see #getRank()
799   */
800  public void setRank(Integer rank) {
801    _rank = rank;
802  }
803
804  /**
805   * @return the mediaType
806   */
807  public MediaType getMediaType() {
808    return _mediaType;
809  }
810
811  /**
812   * @param mediaType the mediaType to set
813   */
814  public void setMediaType(MediaType mediaType) {
815    _mediaType = mediaType;
816  }
817  
818  /**
819   * for serialization
820   * 
821   * @param value
822   * @see #setMediaType(core.tut.pori.utils.MediaUrlValidator.MediaType)
823   */
824  @Field(Definitions.SOLR_FIELD_MEDIA_TYPE)
825  private void setMediaTypeValue(Integer value){
826    setMediaType(value == null ? null : MediaType.fromInt(value));
827  }
828  
829  /**
830   * @see #getMediaType()
831   * 
832   * @return status value
833   */
834  public Integer getMediaTypeValue(){
835    MediaType mediaType = getMediaType();
836    return (mediaType == null ? null : mediaType.toInt());
837  }
838  
839  /**
840   * @return the timecodes
841   */
842  public TimecodeList getTimecodes() {
843    return _timecodes;
844  }
845
846  /**
847   * Solr serializing helper method
848   * @param timecodes the timecodes to set in SolrJ format.
849   */
850  @Field(Definitions.SOLR_FIELD_TIMECODES)
851  public void setSolrTimecodes(List<String> timecodes) {
852    _timecodes = TimecodeList.populateTimecodes(timecodes);
853  }
854  
855  /**
856   * Solr serializing helper method
857   * @return the SolrJ formatted list of timecodes
858   */
859  public List<String> getSolrTimecodes() {
860    return TimecodeList.getSolrTimecodes(_timecodes);
861  }
862  
863  /**
864   * @param timecodes the timecodes to set
865   */
866  public void setTimecodes(TimecodeList timecodes) {
867    _timecodes = timecodes;
868  }
869}