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 core.tut.pori.utils;
017
018import java.text.ParseException;
019import java.util.Date;
020
021import org.apache.commons.lang3.ArrayUtils;
022import org.apache.commons.lang3.time.FastDateFormat;
023import org.apache.log4j.Logger;
024
025
026/**
027 * A thread-safe utility class for processing Strings.
028 */
029public final class StringUtils {
030  private static final FastDateFormat ISO_DATE = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ssZ");
031  private static final Logger LOGGER = Logger.getLogger(StringUtils.class);
032  private static final String ZULU = "Z";
033
034  /**
035   * 
036   */
037  private StringUtils(){
038    // nothing needed
039  }
040
041  /**
042   * 
043   * @param started
044   * @param finished
045   * @return string representation of the time interval between the given dates
046   */
047  public static String getDurationString(Date started, Date finished){    
048    long duration_ms = finished.getTime() - started.getTime();
049    long ms = ((duration_ms % 86400000) % 3600000) % 1000;
050    long s = (((duration_ms % 86400000) % 3600000) % 60000) /1000;
051    long min = ((duration_ms % 86400000) % 3600000)/60000;
052    long h = (duration_ms % 86400000)/3600000;
053    StringBuilder sb = new StringBuilder();
054    if(h > 0){
055      sb.append(String.valueOf(h));
056      sb.append(" h");
057    }
058    if(min > 0){
059      if(sb.length() > 0)
060        sb.append(" ");
061      sb.append(String.valueOf(min));
062      sb.append(" min");
063    }
064    if(s > 0){
065      if(sb.length() > 0)
066        sb.append(" ");
067      sb.append(String.valueOf(s));
068      sb.append(" s");
069    }
070    if(ms > 0){
071      if(sb.length() > 0)
072        sb.append(" ");
073      sb.append(String.valueOf(ms));
074      sb.append(" ms");
075    }
076    if(sb.length() < 1){
077      sb.append(" less than 1 ms");
078    }
079    return sb.toString();
080  }
081
082  /**
083   * This method is synchronized for the conversion
084   * 
085   * @param date
086   * @return null if null passed, otherwise the passed string in format yyyy-MM-dd'T'HH:mm:ssZ
087   */
088  public static String dateToISOString(Date date){
089    return (date == null ? null : ISO_DATE.format(date));
090  }
091  
092  /**
093   * Note: this method will accept microseconds, but the actual microsecond values will be ignored,
094   * passing 2012-05-23T10:32:20.000000Z equals to passing 2012-05-23T10:32:20Z. 
095   * 
096   * Both Z and +XXXX are accepted timezone formats.
097   * 
098   * @param date the passed string in format yyyy-MM-dd'T'HH:mm:ssZ
099   * @return the given string as ISODate or null if the string does not contain a valid date
100   */
101  public static Date ISOStringToDate(String date){
102    try {
103      int pointIndex = date.indexOf('.');
104      if(pointIndex > 0){ // strip milliseconds 2012-05-23T10:32:20.XXXXXXZ
105        if(date.endsWith(ZULU)){ // strip the tailing Z as it creates issues with simple date format
106          date = date.substring(0, pointIndex)+"+0000";
107        }else{
108          int plusIndex = date.indexOf('+', pointIndex);
109          if(plusIndex < 0){
110            LOGGER.error("Invalid date string.");
111            return null;
112          }
113          date = date.substring(0, pointIndex) + date.substring(plusIndex);
114        }
115      }else if(date.endsWith(ZULU)){ // strip the tailing Z as it creates issues with simple date format
116        date = date.substring(0, date.length()-1)+"+0000";
117      }
118      return ISO_DATE.parse(date);  
119    } catch (ParseException ex) {//+0300
120      LOGGER.error(ex, ex);
121      return null;
122    }
123  }
124  
125  /**
126   * Uses DataTypeConverter to parse arbitrary date string to Date.
127   * @see javax.xml.bind.DatatypeConverter#parseDateTime(String)
128   * @param date
129   * @return the parsed date or null if null or empty date was passed
130   */
131  public static Date stringToDate(String date){
132    if(org.apache.commons.lang3.StringUtils.isBlank(date)){
133      return null;
134    }
135    return javax.xml.bind.DatatypeConverter.parseDateTime(date).getTime();
136  }
137  
138  /**
139   * 
140   * @param sb
141   * @param array array of items to append, if empty or null, this is a no-op
142   * @param separator separator used for the join operation, if empty or null the values will appended without separator
143   * @return the passed builder
144   * @throws IllegalArgumentException on bad data
145   */
146  public static StringBuilder append(StringBuilder sb, int[] array, String separator) throws IllegalArgumentException{
147    if(sb == null){
148      throw new IllegalArgumentException("null builder.");
149    }
150    if(ArrayUtils.isEmpty(array)){
151      LOGGER.debug("Ignored empty array.");
152      return sb;
153    }
154    
155    boolean hasSeparator = !org.apache.commons.lang3.StringUtils.isBlank(separator);
156    
157    sb.append(array[0]);
158    for(int i=1;i<array.length;++i){
159      if(hasSeparator){
160        sb.append(separator);
161      }
162      sb.append(array[i]);
163    }
164    return sb;
165  }
166  
167  /**
168   * 
169   * @param sb
170   * @param array array of items to append, if empty or null, this is a no-op
171   * @param separator separator used for the join operation, if empty or null the values will appended without separator
172   * @return the passed builder
173   * @throws IllegalArgumentException on bad data
174   */
175  public static StringBuilder append(StringBuilder sb, long[] array, String separator) throws IllegalArgumentException{
176    if(sb == null){
177      throw new IllegalArgumentException("null builder.");
178    }
179    if(ArrayUtils.isEmpty(array)){
180      LOGGER.debug("Ignored empty array.");
181      return sb;
182    }
183    
184    boolean hasSeparator = !org.apache.commons.lang3.StringUtils.isBlank(separator);
185    
186    sb.append(array[0]);
187    for(int i=1;i<array.length;++i){
188      if(hasSeparator){
189        sb.append(separator);
190      }
191      sb.append(array[i]);
192    }
193    return sb;
194  }
195}