001/**
002 * Copyright 2016 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.apilta.alerts.reference;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.Date;
021import java.util.Random;
022import java.util.Set;
023import java.util.UUID;
024
025import org.apache.commons.collections4.IterableUtils;
026import org.apache.commons.lang3.ArrayUtils;
027import org.apache.commons.lang3.RandomUtils;
028import org.apache.commons.text.RandomStringGenerator;
029import org.apache.log4j.Logger;
030
031import core.tut.pori.http.parameters.DataGroups;
032import core.tut.pori.http.parameters.DateIntervalParameter.Interval;
033import core.tut.pori.http.parameters.Limits;
034import core.tut.pori.users.UserIdentity;
035import service.tut.pori.apilta.alerts.datatypes.Alert;
036import service.tut.pori.apilta.alerts.datatypes.AlertList;
037import service.tut.pori.apilta.alerts.datatypes.Location;
038import service.tut.pori.apilta.files.TemporaryTokenHandler;
039import service.tut.pori.apilta.files.datatypes.Definitions;
040import service.tut.pori.apilta.files.datatypes.FileDetails;
041import service.tut.pori.apilta.files.datatypes.FileDetailsList;
042
043/**
044 * 
045 * Class that can be used to created example objects/object lists.
046 *
047 */
048public class AlertsXMLObjectCreator {
049  private static final String IMAGE_URL = "http://otula.pori.tut.fi/d2i/leijona_album_art.jpg";
050  private static final String IMAGE_URL_MIME_TYPE = "image/jpeg";
051  private static final Logger LOGGER = Logger.getLogger(AlertsXMLObjectCreator.class);  
052  private static final Integer ALERT_RANGE = 100; // in meters
053  private static final int TEXT_LENGTH = 64;
054  private Random _random = null;
055  private RandomStringGenerator _stringGenerator = null;
056
057  /**
058   * 
059   * @param seed for random generator, or null to use default (system time in nanoseconds)
060   */
061  public AlertsXMLObjectCreator(Long seed){
062    if(seed == null){
063      seed = System.nanoTime();
064    }
065    _random = new Random(seed);
066    _stringGenerator = new RandomStringGenerator.Builder().withinRange('a', 'z').build();
067  }
068
069  /**
070   * @return the random
071   */
072  public Random getRandom() {
073    return _random;
074  }
075
076  /**
077   * 
078   * @param alertGroupId 
079   * @param alertType
080   * @param createdFilter 
081   * @param dataGroups 
082   * @param limits
083   * @param location
084   * @param range 
085   * @return alert list
086   */
087  public AlertList generateAlertList(long[] alertGroupId, Collection<String> alertType, Set<Interval> createdFilter, DataGroups dataGroups, Limits limits, Location location, Double range) {
088    int count = limits.getMaxItems(service.tut.pori.apilta.alerts.datatypes.Definitions.ELEMENT_ALERT_LIST);
089    if(count < 1){
090      LOGGER.warn("count < 1");
091      return null;
092    }else if(count >= Limits.DEFAULT_MAX_ITEMS){
093      LOGGER.debug("Count was "+Limits.DEFAULT_MAX_ITEMS+", using 1.");
094      count = 1;
095    }
096    
097    ArrayList<Alert> alerts = new ArrayList<>(count);
098    for(int i=0;i<count;++i){
099      alerts.add(generateAlert(alertGroupId, alertType, createdFilter, dataGroups, limits, location, range));
100    }
101    
102    AlertList list = new AlertList();
103    list.setAlerts(alerts);
104    return list;
105  }
106
107  /**
108   * 
109   * @param alertGroupId 
110   * @param alertType
111   * @param createdFilter 
112   * @param dataGroups 
113   * @param limits 
114   * @param location
115   * @param range 
116   * @return alert
117   */
118  public Alert generateAlert(long[] alertGroupId, Collection<String> alertType, Set<Interval> createdFilter, DataGroups dataGroups, Limits limits, Location location, Double range) {
119    Alert alert = new Alert();
120    
121    ArrayList<Long> ids = new ArrayList<>(1);
122    if(ArrayUtils.isEmpty(alertGroupId)){
123      ids.add(Math.abs(_random.nextLong()));
124    }else{
125      ids.add(alertGroupId[_random.nextInt(alertGroupId.length)]);
126    }
127    alert.setAlertGroupIds(ids);
128    
129    if(alertType != null && !alertType.isEmpty()){
130      alert.setAlertType(IterableUtils.get(alertType, _random.nextInt(alertType.size())));
131    }else{
132      alert.setAlertType(_stringGenerator.generate(TEXT_LENGTH));
133    }
134    
135    alert.setDescription(_stringGenerator.generate(TEXT_LENGTH));
136    alert.setUserId(new UserIdentity(Math.abs(_random.nextLong())));
137    Date created = generateCreatedTimestamp(createdFilter);
138    alert.setCreated(created);
139    alert.setValidUntil(new Date(System.currentTimeMillis()+6000000)); // make sure the alert is still valid by creating the timestamp sometime in the future
140    alert.setLocation(generateLocation(location, range));
141    alert.setAlertId(UUID.randomUUID().toString());
142    alert.setRange(ALERT_RANGE);
143    
144    if(DataGroups.hasDataGroup(DataGroups.DATA_GROUP_ALL, dataGroups)){
145      alert.setFiles(generateFileDetailsList(limits));
146    }
147    
148    return alert;
149  }
150  
151  /**
152   * 
153   * @param source if null, random location is returned
154   * @param range 
155   * @return randomly generated location located within the given range of the location source
156   */
157  public Location generateLocation(Location source, Double range) {
158    Location location = new Location();
159    if(source == null){
160      if(range != null){
161        LOGGER.debug("Ignoring range because of null source.");
162      }
163      location.setLatitude(_random.nextDouble()*181-90);
164      location.setLongitude(_random.nextDouble()*361-180);
165    }else{  
166      Double sourceHeading = source.getHeading();
167      double heading = Math.toRadians(sourceHeading == null ? _random.nextInt(360) : sourceHeading);
168      double r = (range == null ? _random.nextInt(service.tut.pori.apilta.alerts.Definitions.DEFAULT_RANGE.intValue()) : range);
169      
170      double lat = source.getLatitude();
171      double lon = source.getLongitude();
172      
173      lat += Math.cos(heading) * r;
174      if(lat < -90){ // simply limit the value in case invalid coordinate is generated
175        lat = -90;
176      }else if(lat > 90){
177        lat = 90;
178      }
179      
180      lon += Math.sin(heading) * r;
181      if(lon < -180){ // simply limit the value in case invalid coordinate is generated
182        lon = -180;
183      }else if(lon > 180){
184        lon = 180;
185      }
186      
187      location.setLatitude(lat);
188      location.setLongitude(lon);
189    }
190    return location;
191  }
192  
193  /**
194   * 
195   * @param limits 
196   * @return randomly generated file details list
197   */
198  public FileDetailsList generateFileDetailsList(Limits limits) {
199    int count = limits.getMaxItems(Definitions.ELEMENT_FILE_DETAILS_LIST);
200    if(count < 1){
201      LOGGER.warn("count < 1");
202      return null;
203    }else if(count >= Limits.DEFAULT_MAX_ITEMS){
204      LOGGER.debug("Count was "+Limits.DEFAULT_MAX_ITEMS+", using 1.");
205      count = 1;
206    }
207    
208    ArrayList<FileDetails> details = new ArrayList<>(count);
209    for(int i=0;i<count;++i){
210      details.add(generateFileDetails());
211    }
212    FileDetailsList list = new FileDetailsList();
213    list.setFiles(details);
214    return list;
215  }
216  
217  /**
218   * 
219   * @return randomly generated file details
220   */
221  public FileDetails generateFileDetails() {
222    FileDetails details = new FileDetails();
223    details.setGUID(UUID.randomUUID().toString());
224    details.setMimeType(IMAGE_URL_MIME_TYPE);
225    details.setUrl(IMAGE_URL);
226    details.setValidUntil(generateValidUntil());
227    return details;
228  }
229  
230  /**
231   * 
232   * @param createdFilter If multiple values are given, the interval will be selected randomly from the given values. If null, a random time between unix epoch and the current time is selected
233   * @return randomly generate date between UNIX epoch and current time
234   */
235  public Date generateCreatedTimestamp(Set<Interval> createdFilter) {
236    if(createdFilter == null || createdFilter.isEmpty()){
237      return new Date(RandomUtils.nextLong(0, System.currentTimeMillis()));
238    }else{
239      Interval interval = IterableUtils.get(createdFilter, _random.nextInt(createdFilter.size()));
240      return new Date(RandomUtils.nextLong(interval.getStart().getTime(), interval.getEnd().getTime()));
241    }
242  }
243  
244  /**
245   * 
246   * @return timestamp sometime in the future
247   */
248  public Date generateValidUntil() {
249    return new Date(TemporaryTokenHandler.CACHE_VALIDITY*1000+System.currentTimeMillis());
250  }
251
252  /**
253   * 
254   * @return randomly generated GUID
255   */
256  public String createGUID() {
257    return UUID.randomUUID().toString();
258  }
259}