001/**
002 * Copyright 2015 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.video;
017
018import java.util.ArrayList;
019import java.util.List;
020
021import javax.xml.bind.annotation.XmlAccessType;
022import javax.xml.bind.annotation.XmlAccessorType;
023import javax.xml.bind.annotation.XmlElement;
024import javax.xml.bind.annotation.XmlRootElement;
025
026import org.apache.commons.lang3.StringUtils;
027import org.apache.log4j.Logger;
028
029/**
030 * A list of media object time codes.
031 * 
032 * <h3>XML Example</h3>
033 * 
034 * {@doc.restlet service="[service.tut.pori.contentanalysis.video.reference.Definitions#SERVICE_VCA_REFERENCE_EXAMPLE]" method="[service.tut.pori.contentanalysis.video.Definitions#ELEMENT_TIMECODELIST]" type="GET" query="" body_uri=""}
035 * 
036 * @see service.tut.pori.contentanalysis.video.Timecode
037 */
038@XmlRootElement(name=Definitions.ELEMENT_TIMECODELIST)
039@XmlAccessorType(value=XmlAccessType.NONE)
040public class TimecodeList {
041  private static final Logger LOGGER = Logger.getLogger(TimecodeList.class);
042  private static final String SOLR_SEPARATOR = ";";
043  @XmlElement(name = Definitions.ELEMENT_TIMECODE)
044  private List<Timecode> _timecodes = null;
045
046  /**
047   * @return the timecodes
048   * @see #setTimecodes(List)
049   */
050  public List<Timecode> getTimecodes() {
051    return _timecodes;
052  }
053
054  /**
055   * @param timecodes the timecodes to set
056   * @see #getTimecodes()
057   */
058  public void setTimecodes(List<Timecode> timecodes) {
059    _timecodes = timecodes;
060  }
061  
062  /**
063   * Only for sub-classing, use the static.
064   * 
065   * @return true if the list contains no items
066   * @see #isEmpty(TimecodeList)
067   */
068  protected boolean isEmpty() {
069    return (_timecodes == null ? true : _timecodes.isEmpty());
070  }
071  
072  /**
073   * Only for sub-classing, use the static.
074   * 
075   * @return true if the list contains only valid values
076   * @see #isValid(TimecodeList)
077   */
078  protected boolean isValid() {
079    if(isEmpty()){
080      return false;
081    }
082    for(Timecode tc : _timecodes){
083      if(!Timecode.isValid(tc)){
084        return false;
085      }
086    }
087    return true;
088  }
089
090  /**
091   * 
092   * @param timecodes
093   * @return true if the passed list is not null, not empty, and valid
094   */
095  public static boolean isValid(TimecodeList timecodes) {
096    return (timecodes == null ? false : timecodes.isValid());
097  }
098  
099  /**
100   * 
101   * @param timecodes
102   * @return true if the passed list is null, empty or contains no items
103   */
104  public static boolean isEmpty(TimecodeList timecodes) {
105    return (timecodes == null ? true : timecodes.isEmpty());
106  }
107  
108  /**
109   * 
110   * @param solrTimecodes if null or empty, this does nothing
111   * @return a populated timecode list
112   * @throws IllegalArgumentException on bad data
113   */
114  public static TimecodeList populateTimecodes(List<String> solrTimecodes) throws IllegalArgumentException{
115    if(solrTimecodes == null || solrTimecodes.isEmpty()){
116      LOGGER.debug("No timecodes given.");
117      return null;
118    }
119    List<Timecode> timecodes = new ArrayList<>(solrTimecodes.size());
120    for(String solrTimecode : solrTimecodes){
121      String codes[] = StringUtils.split(solrTimecode, SOLR_SEPARATOR);
122      if(codes.length<2){
123        LOGGER.warn("Invalid timecode ignored.");
124        continue;
125      }
126      try{
127        Timecode code = new Timecode();
128        code.setStart(Double.valueOf(codes[0]));
129        code.setEnd(Double.valueOf(codes[1]));
130        timecodes.add(code);
131      }catch(NumberFormatException ex){
132        LOGGER.debug(ex,ex);
133        throw new IllegalArgumentException("Malformed timecode data.");
134      }
135    }
136    if(timecodes.isEmpty()){
137      return null;
138    }else{
139      TimecodeList timecodeList = new TimecodeList();
140      timecodeList.setTimecodes(timecodes);
141      return timecodeList;
142    }
143  }
144
145  /**
146   * 
147   * @param timecodes if null or empty this does nothing
148   * @return list of combined timecodes [{start;end}]
149   * @throws IllegalArgumentException on missing values
150   */
151  public static List<String> getSolrTimecodes(TimecodeList timecodes) throws IllegalArgumentException{
152    if(timecodes == null || timecodes.isEmpty()){
153      LOGGER.debug("No timecodes given.");
154      return null;
155    }
156    List<String> retval = new ArrayList<>(timecodes.getTimecodes().size());
157    for(Timecode timecode : timecodes.getTimecodes()){
158      if(timecode == null || timecode.getStart() == null || timecode.getEnd() == null){
159        throw new IllegalArgumentException("Timecode data was missing.");
160      }
161      retval.add(timecode.getStart().toString()+SOLR_SEPARATOR+timecode.getEnd().toString());
162    }
163    return retval;
164  }
165
166  /**
167   * Add new timecode. This will NOT check for duplicates.
168   * 
169   * @param timecode
170   */
171  public void addTimecode(Timecode timecode) {
172    if(_timecodes == null){
173      _timecodes = new ArrayList<>();
174    }
175    _timecodes.add(timecode);
176  }
177}