001    package org.hackystat.projectbrowser.page.trajectory;
002    
003    import java.io.Serializable;
004    import java.util.ArrayList;
005    import java.util.Collections;
006    import java.util.Date;
007    import java.util.HashMap;
008    import java.util.List;
009    import java.util.Map;
010    import java.util.logging.Level;
011    import java.util.logging.Logger;
012    
013    import org.apache.wicket.Application;
014    import org.apache.wicket.PageParameters;
015    import org.apache.wicket.model.IModel;
016    import org.apache.wicket.model.Model;
017    import org.hackystat.projectbrowser.ProjectBrowserApplication;
018    import org.hackystat.projectbrowser.ProjectBrowserSession;
019    import org.hackystat.projectbrowser.page.trajectory.datapanel.TrajectoryChartDataModel;
020    import org.hackystat.sensorbase.resource.projects.jaxb.Project;
021    import org.hackystat.telemetry.service.client.TelemetryClient;
022    import org.hackystat.telemetry.service.client.TelemetryClientException;
023    import org.hackystat.telemetry.service.resource.chart.jaxb.ParameterDefinition;
024    import org.hackystat.telemetry.service.resource.chart.jaxb.TelemetryChartDefinition;
025    import org.hackystat.telemetry.service.resource.chart.jaxb.TelemetryChartRef;
026    import org.hackystat.telemetry.service.resource.chart.jaxb.Type;
027    import org.hackystat.utilities.logger.HackystatLogger;
028    import org.hackystat.utilities.tstamp.Tstamp;
029    
030    /**
031     * Session to hold state for trajectory.
032     * 
033     * @author Pavel Senin
034     * @author Shaoxuan Zhang
035     */
036    public class TrajectorySession implements Serializable {
037    
038      /** Support serialization. */
039      private static final long serialVersionUID = 1L;
040    
041      /** The parameter key of selectedProject1. */
042      public static final String SELECTED_PROJECT1_KEY = "project1";
043      /** The parameter key of start date of project 1. */
044      public static final String START_DATE1_KEY = "startdate1";
045      /** The parameter key of end dateof project 1. */
046      public static final String END_DATE1_KEY = "enddate1";
047    
048      /** The parameter key of selectedProject1. */
049      public static final String SELECTED_PROJECT2_KEY = "project2";
050      /** The parameter key of start date of project 1. */
051      public static final String START_DATE2_KEY = "startdate2";
052      /** The parameter key of end dateof project 1. */
053      public static final String END_DATE2_KEY = "enddate2";
054    
055      /** The parameter key of telemetry. */
056      public static final String TELEMETRY_KEY = "telemetry";
057      /** The parameter key of granularity. */
058      public static final String GRANULARITY_KEY = "granularity";
059      /** The parameter key of telemetry parameters. */
060      public static final String TELEMETRY_PARAMERTERS_KEY = "param";
061    
062      /** The separator for parameter values. */
063      public static final String PARAMETER_VALUE_SEPARATOR = ",";
064      /** The separator between project name and its onwer. */
065      public static final String PROJECT_NAME_OWNER_SEPARATR = "-";
066    
067      /** The project #1 this user has selected. */
068      private ProjectRecord selectedProject1 = new ProjectRecord();
069      // /** The start date this user has selected. */
070      // private long project1StartDate = ProjectBrowserBasePage.getDateBefore(7).getTime();
071      // /** The end date this user has selected. */
072      // private long project1EndDate = ProjectBrowserBasePage.getDateBefore(1).getTime();
073    
074      /** The project #2 this user has selected. */
075      private ProjectRecord selectedProject2 = new ProjectRecord();
076      // /** The start date this user has selected. */
077      // private long project2StartDate = ProjectBrowserBasePage.getDateBefore(7).getTime();
078      // /** The end date this user has selected. */
079      // private long project2EndDate = ProjectBrowserBasePage.getDateBefore(1).getTime();
080    
081      /** The analysis this user has selected. */
082      private String telemetryName = null;
083      /** The descriptions for all telemetries. */
084      private final Map<String, TelemetryChartDefinition> telemetrys 
085                                                      = new HashMap<String, TelemetryChartDefinition>();
086      /** The granularity of the chart. Either Day, Week, or Month. */
087      private String granularity = "Day";
088      /** The available granularities. */
089      private final List<String> granularityList = new ArrayList<String>();
090      /** the feedback string. */
091      private String feedback = "";
092      /** The parameters for telemetry chart. */
093      private List<IModel> parameters = new ArrayList<IModel>();
094      /** The data model to hold state for data panel. */
095      private TrajectoryChartDataModel dataModel = new TrajectoryChartDataModel();
096      /** Error message when parsing page paramters. */
097      private String paramErrorMessage = "";
098    
099      /** Since I'm reusing the telemetry code, I'm keeping the way to communicate using list. */
100      private List<ProjectRecord> projectList = null;
101    
102      private String dtwWindowType;
103    
104      private String dtwStep;
105    
106      private String dtwOpenEndType;
107    
108      private Integer dtwWindowSize;
109    
110      // Some string constants used while logging
111      //
112      private static final String SP = " ";
113      private static final String CR = "\n";
114      private static final String IDNT = "                ";
115      private static final String IDNT2 = "                  ";
116      private static final String MARK = "[DEBUG] ";
117      private static final String ERROR_URL_PARAM = "Error URL parameter: project: ";
118      private static final String R_HAND = " > ";
119    
120      // making life easy - preselecting the interval dates
121      //
122      private static final String project1StartString = "2008-01-01";
123      private static final String project1EndString = "2008-01-15";
124    
125      private static final String project2StartString = "2008-03-01";
126      private static final String project2EndString = "2008-03-15";
127    
128      /**
129       * Constructor - create the session instance.
130       */
131      public TrajectorySession() {
132        // elevate the logging level here
133        Logger logger = HackystatLogger.getLogger("org.hackystat.projectbrowser", "projectbrowser");
134        HackystatLogger.setLoggingLevel(logger, "FINER");
135        
136        granularityList.add("Day");
137        granularityList.add("Week");
138        granularityList.add("Month");
139        try {
140          this.selectedProject1.setStartDate(Tstamp.makeTimestamp(project1StartString)
141              .toGregorianCalendar().getTimeInMillis());
142          this.selectedProject1.setEndDate(Tstamp.makeTimestamp(project1EndString)
143              .toGregorianCalendar().getTimeInMillis());
144          this.selectedProject2.setStartDate(Tstamp.makeTimestamp(project2StartString)
145              .toGregorianCalendar().getTimeInMillis());
146          this.selectedProject2.setEndDate(Tstamp.makeTimestamp(project2EndString)
147              .toGregorianCalendar().getTimeInMillis());
148        }
149        catch (Exception e) {
150          e.printStackTrace();
151        }
152      }
153    
154      /**
155       * Returns the list of Projects associated with this user.
156       * 
157       * @return The list of Projects.
158       */
159      public List<ProjectRecord> getProjectList() {
160        if (this.projectList == null || this.projectList.size() <= 0) {
161          projectList = new ArrayList<ProjectRecord>();
162          List<Project> projects = ProjectBrowserSession.get().getProjectList();
163          for (Project p : projects) {
164            ProjectRecord pr = new ProjectRecord(p, 0);
165            projectList.add(pr);
166          }
167        }
168        return projectList;
169      }
170    
171      /**
172       * @return the parameters
173       */
174      public List<IModel> getParameters() {
175        return this.parameters;
176      }
177    
178      /**
179       * Returns the list of the parameters in a single String, separated by comma.
180       * 
181       * @return a String.
182       */
183      public String getParametersAsString() {
184        StringBuffer stringBuffer = new StringBuffer();
185        for (IModel model : this.parameters) {
186          if (model != null) {
187            stringBuffer.append(model.getObject());
188            stringBuffer.append(PARAMETER_VALUE_SEPARATOR);
189          }
190        }
191        String param = stringBuffer.toString();
192        if (param.endsWith(PARAMETER_VALUE_SEPARATOR)) {
193          param = param.substring(0, param.length() - 1);
194        }
195        return param;
196      }
197    
198      /**
199       * @param feedback the feedback to set
200       */
201      public void setFeedback(String feedback) {
202        this.feedback = feedback;
203      }
204    
205      /**
206       * @return the feedback
207       */
208      public String getFeedback() {
209        String returnString = this.feedback;
210        this.feedback = "";
211        return returnString;
212      }
213    
214      /**
215       * @param telemetryName the telemetry to set
216       */
217      public void setTelemetryName(String telemetryName) {
218        this.telemetryName = telemetryName;
219      }
220    
221      /**
222       * @return the telemetry
223       */
224      public String getTelemetryName() {
225        return this.telemetryName;
226      }
227    
228      /**
229       * Get the logger.
230       * 
231       * @return the logger that associated to this web application.
232       */
233      private Logger getLogger() {
234        return ((ProjectBrowserApplication) Application.get()).getLogger();
235      }
236    
237      /**
238       * Return the TelemetryList. Returns empty list if no telemetry streams data collected.
239       * 
240       * @return the telemetryList
241       */
242      public List<String> getTelemetryList() {
243        Logger logger = getLogger();
244        List<String> telemetryList = new ArrayList<String>();
245        if (this.getTelemetrys().isEmpty()) {
246          TelemetryClient client = ProjectBrowserSession.get().getTelemetryClient();
247          try {
248            logger.info("Retrieving data for Telemetry chart definitions.");
249            for (TelemetryChartRef chartRef : client.getChartIndex().getTelemetryChartRef()) {
250              getTelemetrys().put(chartRef.getName(), client.getChartDefinition(chartRef.getName()));
251            }
252            logger.info("Finished retrieving data for Telemetry chart definitions.");
253          }
254          catch (TelemetryClientException e) {
255            this.feedback = "Exception when retrieving Telemetry chart definition: " + e.getMessage();
256            logger.warning("Error when retrieving Telemetry chart definition: " + e.getMessage());
257          }
258        }
259        telemetryList.addAll(this.getTelemetrys().keySet());
260        Collections.sort(telemetryList);
261        return telemetryList;
262      }
263    
264      /**
265       * Return the list of ParameterDefinition under telemetry type in this session.
266       * 
267       * @return list of ParameterDefinition.
268       */
269      public List<ParameterDefinition> getParameterList() {
270        Logger logger = getLogger();
271        if (this.telemetryName != null) {
272          TelemetryChartDefinition teleDef = this.getTelemetrys().get(telemetryName);
273          if (teleDef == null) {
274            TelemetryClient client = ProjectBrowserSession.get().getTelemetryClient();
275            try {
276              logger.info("Retrieving chart definition of telemetry: " + this.telemetryName);
277              teleDef = client.getChartDefinition(this.telemetryName);
278              this.getTelemetrys().put(telemetryName, teleDef);
279              return teleDef.getParameterDefinition();
280            }
281            catch (TelemetryClientException e) {
282              this.feedback = "Error when retrieving chart definition of telemetry: "
283                  + this.telemetryName + ">>" + e.getMessage();
284              logger.warning("Error when retrieving chart definition of telemetry: "
285                  + this.telemetryName + ">>" + e.getMessage());
286            }
287          }
288          else {
289            return teleDef.getParameterDefinition();
290          }
291        }
292        return new ArrayList<ParameterDefinition>();
293      }
294    
295      /**
296       * @param startDate the startDate to set
297       */
298      public void setProject1StartDate(Date startDate) {
299        this.selectedProject1.setStartDate(startDate.getTime());
300      }
301    
302      /**
303       * @return the startDate
304       */
305      public Date getProject1StartDate() {
306        return new Date(this.selectedProject1.getStartDate().getTime());
307      }
308    
309      /**
310       * @param endDate the endDate to set
311       */
312      public void setProject1EndDate(Date endDate) {
313        this.selectedProject1.setEndDate(endDate.getTime());
314      }
315    
316      /**
317       * @return the endDate
318       */
319      public Date getProject1EndDate() {
320        return new Date(this.selectedProject1.getEndDate().getTime());
321      }
322    
323      /**
324       * @param startDate the startDate to set
325       */
326      public void setProject2StartDate(Date startDate) {
327        this.selectedProject2.setStartDate(startDate.getTime());
328      }
329    
330      /**
331       * @return the startDate
332       */
333      public Date getProject2StartDate() {
334        return new Date(this.selectedProject2.getStartDate().getTime());
335      }
336    
337      /**
338       * @param endDate the endDate to set
339       */
340      public void setProject2EndDate(Date endDate) {
341        this.selectedProject2.setEndDate(endDate.getTime());
342      }
343    
344      /**
345       * @return the endDate
346       */
347      public Date getProject2EndDate() {
348        return new Date(this.selectedProject2.getEndDate().getTime());
349      }
350    
351      /**
352       * Returns the string that represents startDate in standard formatted. e.g.
353       * 2008-08-08T08:08:08+08:00, the +08:00 in the end means the time zone of this time stamp is
354       * +08:00
355       * 
356       * @return a String
357       */
358      public String getFormattedProject1StartDateString() {
359        return Tstamp.makeTimestamp(this.selectedProject1.getStartDate().getTime()).toString();
360      }
361    
362      /**
363       * Returns the string that represents endDate in standard formatted. e.g.
364       * 2008-08-08T08:08:08+08:00, the +08:00 in the end means the time zone of this time stamp is
365       * +08:00
366       * 
367       * @return a String
368       */
369      public String getFormattedProject1EndDateString() {
370        return Tstamp.makeTimestamp(this.selectedProject1.getEndDate().getTime()).toString();
371      }
372    
373      /**
374       * Returns the string that represents startDate in standard formatted. e.g.
375       * 2008-08-08T08:08:08+08:00, the +08:00 in the end means the time zone of this time stamp is
376       * +08:00
377       * 
378       * @return a String
379       */
380      public String getFormattedProject2StartDateString() {
381        return Tstamp.makeTimestamp(this.selectedProject2.getStartDate().getTime()).toString();
382      }
383    
384      /**
385       * Returns the string that represents endDate in standard formatted. e.g.
386       * 2008-08-08T08:08:08+08:00, the +08:00 in the end means the time zone of this time stamp is
387       * +08:00
388       * 
389       * @return a String
390       */
391      public String getFormattedProject2EndDateString() {
392        return Tstamp.makeTimestamp(this.selectedProject2.getEndDate().getTime()).toString();
393      }
394    
395      /**
396       * Set the project1 indent.
397       * 
398       * @param project1Indent The indent to set.
399       */
400      public void setProject1Indent(Integer project1Indent) {
401        this.selectedProject1.setIndent(project1Indent);
402      }
403    
404      /**
405       * Get the project1 indent.
406       * 
407       * @return The project1 indent.
408       */
409      public Integer getProject1Indent() {
410        return this.selectedProject1.getIndent();
411      }
412    
413      /**
414       * Set the project2 indent.
415       * 
416       * @param project2Indent The indent to set.
417       */
418      public void setProject2Indent(Integer project2Indent) {
419        this.selectedProject2.setIndent(project2Indent);
420      }
421    
422      /**
423       * Get the project2 indent.
424       * 
425       * @return The project2 indent.
426       */
427      public Integer getProject2Indent() {
428        return this.selectedProject2.getIndent();
429      }
430    
431      /**
432       * @param granularity the granularity to set
433       */
434      public void setGranularity(String granularity) {
435        this.granularity = granularity;
436      }
437    
438      /**
439       * @return the granularity
440       */
441      public String getGranularity() {
442        return granularity;
443      }
444    
445      /**
446       * @return the granularityList
447       */
448      public List<String> getGranularityList() {
449        return granularityList;
450      }
451    
452      /**
453       * Update the data model.
454       */
455      public void updateDataModel() {
456        getLogger().log(
457            Level.FINER,
458            MARK + "TrajectorySession: " + "setting TrajectoryChartDataModel with next parameters:" +
459    
460            CR + IDNT + "selectedProject1: " + getSelectedProject1().getProject().getName() + CR + IDNT
461                + IDNT2 + "startDate: " + getProject1StartDate() + CR + IDNT + IDNT2 + "endDate: "
462                + getProject1EndDate() + CR + IDNT + IDNT2 + "indent: " + getProject1Indent() +
463    
464                CR + IDNT + "selectedProject2: " + getSelectedProject2().getProject().getName() + CR
465                + IDNT + IDNT2 + "startDate: " + getProject2StartDate() + CR + IDNT + IDNT2
466                + "endDate: " + getProject2EndDate() + CR + IDNT + IDNT2 + "indent: "
467                + getProject2Indent() +
468    
469                CR + IDNT + "telemetry: " + telemetryName + CR + IDNT + "granularity: " + granularity
470                + CR + IDNT + "parameters: " + CR + parametersToLog(parameters));
471    
472        this.dataModel.setModel(getSelectedProject1(), getSelectedProject2(), telemetryName,
473            granularity, parameters);
474        Thread thread = new Thread() {
475          @Override
476          public void run() {
477            dataModel.loadData();
478          }
479        };
480        thread.start();
481      }
482    
483      /**
484       * Cancel data model's update.
485       */
486      public void cancelDataUpdate() {
487        getLogger().info(
488            MARK + "TrajectorySession " + this.hashCode() + " cancelling updateDataModel()");
489        dataModel.cancelDataLoading();
490      }
491    
492      /**
493       * Set the data model.
494       * 
495       * @param dataModel the dataModel to set
496       */
497      public void setDataModel(TrajectoryChartDataModel dataModel) {
498        this.dataModel = dataModel;
499      }
500    
501      /**
502       * Get the data model.
503       * 
504       * @return the dataModel
505       */
506      public TrajectoryChartDataModel getDataModel() {
507        return dataModel;
508      }
509    
510      /**
511       * Set the project1.
512       * 
513       * @param rec the selectedProjects to set
514       */
515      public void setSelectedProject1(ProjectRecord rec) {
516        this.selectedProject1 = rec;
517      }
518    
519      /**
520       * Set the project1.
521       * 
522       * @return the project1.
523       */
524      public ProjectRecord getSelectedProject1() {
525        return selectedProject1;
526      }
527    
528      /**
529       * Set the project2.
530       * 
531       * @param rec the selectedProjects to set
532       */
533      public void setSelectedProject2(ProjectRecord rec) {
534        this.selectedProject2 = rec;
535      }
536    
537      /**
538       * Set the project2.
539       * 
540       * @return the project2.
541       */
542      public ProjectRecord getSelectedProject2() {
543        return selectedProject2;
544      }
545    
546      /**
547       * Returns the list of the selected projects in a single String, separated by comma.
548       * 
549       * @return a String.
550       */
551      public String getSelectedProject1AsString() {
552        if (null == selectedProject1) {
553          return "null";
554        }
555        else {
556          StringBuffer projectStr = new StringBuffer();
557          projectStr.append(selectedProject1.getProject().getName());
558          projectStr.append(PROJECT_NAME_OWNER_SEPARATR);
559          projectStr.append(selectedProject1.getProject().getOwner());
560          return projectStr.toString();
561        }
562      }
563    
564      /**
565       * Returns the list of the selected projects in a single String, separated by comma.
566       * 
567       * @return a String.
568       */
569      public String getSelectedProject2AsString() {
570        if (null == selectedProject2) {
571          return "null";
572        }
573        else {
574          StringBuffer projectStr = new StringBuffer();
575          projectStr.append(selectedProject2.getProject().getName());
576          projectStr.append(PROJECT_NAME_OWNER_SEPARATR);
577          projectStr.append(selectedProject2.getProject().getOwner());
578          return projectStr.toString();
579        }
580      }
581    
582      /**
583       * @return the telemetrys
584       */
585      public Map<String, TelemetryChartDefinition> getTelemetrys() {
586        return telemetrys;
587      }
588    
589      /**
590       * @return the list of TelemetryChartDefinition.
591       */
592      public List<TelemetryChartDefinition> getChartDescriptions() {
593        List<TelemetryChartDefinition> chartDef = new ArrayList<TelemetryChartDefinition>();
594        for (String telemetryName : this.getTelemetryList()) {
595          chartDef.add(this.telemetrys.get(telemetryName));
596        }
597        return chartDef;
598      }
599    
600      /**
601       * Returns a Project instance that is available to current user and is matched to the given
602       * project name and project owner.
603       * 
604       * @param projectName the given project name.
605       * @param projectOwner the given project owner.
606       * @return the Project instance. null if no matching project is found, which may means either the
607       *         project name or project owner is null or there is no Project for this user with the
608       *         same project name and owner as the given ones.
609       */
610      public Project getProject(String projectName, String projectOwner) {
611        if (projectName == null || projectOwner == null) {
612          return null;
613        }
614        for (Project project : ProjectBrowserSession.get().getProjectList()) {
615          if (projectName.equals(project.getName()) && projectOwner.equals(project.getOwner())) {
616            return project;
617          }
618        }
619        return null;
620      }
621    
622      /**
623       * Reports a list of available DTW steps in this implementation.
624       * 
625       * @return the list of steps to use in the display menu.
626       */
627      public List<String> getAvailableDTWSteps() {
628        List<String> steps = new ArrayList<String>();
629        steps.add("symmetric1");
630        steps.add("symmetric2");
631        steps.add("asymmetric");
632        Collections.sort(steps);
633        return steps;
634      }
635    
636      /**
637       * Set particular DTW step to use in the analysis.
638       * 
639       * @param step the step to set.
640       */
641      public void setDTWStep(String step) {
642        this.dtwStep = step;
643      }
644    
645      /**
646       * Get the set step function.
647       * 
648       * @return the step function set for analysis.
649       */
650      public String getDTWStep() {
651        return this.dtwStep;
652      }
653    
654      /**
655       * Get the list of available implementations of DTW constraints.
656       * 
657       * @return the list of available DTW constraints functions to use in the form menu.
658       */
659      public List<String> getAvailableWindowTypes() {
660        List<String> windows = new ArrayList<String>();
661        windows.add("No Window");
662        windows.add("SakoeChiba");
663        windows.add("Slanted Band");
664        windows.add("Itakura Window");
665        // Collections.sort(windows);
666        return windows;
667      }
668    
669      /**
670       * Set the specific constraint function.
671       * 
672       * @param constraint the constraint function to set.
673       */
674      public void setWindowType(String constraint) {
675        this.dtwWindowType = constraint;
676      }
677    
678      /**
679       * Get the set constraint function for the DTW analysis.
680       * 
681       * @return the set function.
682       */
683      public String getWindowType() {
684        return this.dtwWindowType;
685      }
686    
687      /**
688       * Set the window (band) size for the constraint function.
689       * 
690       * @param size the window (band) size.
691       */
692      public void setWindowSize(Integer size) {
693        this.dtwWindowSize = size;
694      }
695    
696      /**
697       * Get the window (band) size for constraint function.
698       * 
699       * @return the window size.
700       */
701      public Integer getWindowSize() {
702        return this.dtwWindowSize;
703      }
704    
705      /**
706       * Returns the list of available DTW open-end alignment implementations.
707       * 
708       * @return the list of available alignment implementations to use in the form menu.
709       */
710      public List<String> getAvailableOpenEndTypes() {
711        List<String> openEnds = new ArrayList<String>();
712        openEnds.add("Open Begin");
713        openEnds.add("Open End");
714        // Collections.sort(openEnds);
715        return openEnds;
716      }
717    
718      /**
719       * Set the specific opend-end alignment implementation.
720       * 
721       * @param openEnd the open-end alignment implementation.
722       */
723      public void setOpenEndType(String openEnd) {
724        this.dtwOpenEndType = openEnd;
725      }
726    
727      /**
728       * Get the open-end alignment set for DTW analysis.
729       * 
730       * @return the open-end alignment implementation.
731       */
732      public String getOpenEndType() {
733        return this.dtwOpenEndType;
734      }
735    
736      /**
737       * Load data from URL parameters into this session.
738       * 
739       * @param parameters the URL parameters
740       * @return true if all parameters are loaded correctly
741       */
742      public boolean loadPageParameters(PageParameters parameters) {
743        boolean isLoadSucceed = true;
744        boolean isTelemetryLoaded = false;
745        StringBuffer errorMessage = new StringBuffer(1000);
746        Logger logger = this.getLogger();
747    
748        // load selected projects
749        //
750        if (parameters.containsKey(SELECTED_PROJECT1_KEY)) {
751          String projectString = parameters.getString(SELECTED_PROJECT1_KEY);
752          String[] projectInfo = projectString.split(PROJECT_NAME_OWNER_SEPARATR, 2);
753          if (projectInfo.length < 1) {
754            isLoadSucceed = false;
755            String error = ERROR_URL_PARAM + projectString
756                + " >> project name and owner are missing or not formatted correctly.";
757            logger.warning(error);
758            errorMessage.append(error);
759            errorMessage.append('\n');
760          }
761          String projectName = projectInfo[0];
762          String projectOwner = projectInfo[1];
763          Project project = this.getProject(projectName, projectOwner);
764          if (project == null) {
765            isLoadSucceed = false;
766            String error = ERROR_URL_PARAM + projectString
767                + " >> matching project not found under user: "
768                + ProjectBrowserSession.get().getEmail();
769            logger.warning(error);
770            errorMessage.append(error);
771            errorMessage.append('\n');
772          }
773          else {
774            selectedProject1 = new ProjectRecord(project, 0);
775          }
776        }
777        else {
778          isLoadSucceed = false;
779          errorMessage.append("projects key is missing in URL parameters.\n");
780        }
781        // load start date
782        if (parameters.containsKey(START_DATE1_KEY)) {
783          String startDateString = parameters.getString(START_DATE1_KEY);
784          try {
785            this.selectedProject1.setStartDate(Tstamp.makeTimestamp(startDateString)
786                .toGregorianCalendar().getTimeInMillis());
787          }
788          catch (Exception e) {
789            isLoadSucceed = false;
790            String error = "Errors when parsing start date from URL parameter: " + startDateString;
791            logger.warning(error + R_HAND + e.getMessage());
792            errorMessage.append(error);
793            errorMessage.append('\n');
794          }
795        }
796        else {
797          isLoadSucceed = false;
798          errorMessage.append("startDate key is missing in URL parameters.");
799        }
800        // load end date
801        if (parameters.containsKey(END_DATE1_KEY)) {
802          String endDateString = parameters.getString(END_DATE1_KEY);
803          try {
804            this.selectedProject1.setEndDate(Tstamp.makeTimestamp(endDateString).toGregorianCalendar()
805                .getTimeInMillis());
806          }
807          catch (Exception e) {
808            isLoadSucceed = false;
809            String error = "Errors when parsing end date from URL parameter: " + endDateString;
810            logger.warning(error + R_HAND + e.getMessage());
811            errorMessage.append(error);
812            errorMessage.append('\n');
813          }
814        }
815        else {
816          isLoadSucceed = false;
817          errorMessage.append("endDate key is missing in URL parameters.\n");
818        }
819    
820        // load selected projects
821        //
822        if (parameters.containsKey(SELECTED_PROJECT2_KEY)) {
823          String projectString = parameters.getString(SELECTED_PROJECT2_KEY);
824          String[] projectInfo = projectString.split(PROJECT_NAME_OWNER_SEPARATR, 2);
825          if (projectInfo.length < 1) {
826            isLoadSucceed = false;
827            String error = ERROR_URL_PARAM + projectString
828                + " >> project name and owner are missing or not formatted correctly.";
829            logger.warning(error);
830            errorMessage.append(error);
831            errorMessage.append('\n');
832          }
833          String projectName = projectInfo[0];
834          String projectOwner = projectInfo[1];
835          Project project = this.getProject(projectName, projectOwner);
836          if (project == null) {
837            isLoadSucceed = false;
838            String error = ERROR_URL_PARAM + projectString
839                + " >> matching project not found under user: "
840                + ProjectBrowserSession.get().getEmail();
841            logger.warning(error);
842            errorMessage.append(error);
843            errorMessage.append('\n');
844          }
845          else {
846            selectedProject2 = new ProjectRecord(project, 0);
847          }
848        }
849        else {
850          isLoadSucceed = false;
851          errorMessage.append("projects key is missing in URL parameters.\n");
852        }
853        // load start date
854        if (parameters.containsKey(START_DATE2_KEY)) {
855          String startDateString = parameters.getString(START_DATE2_KEY);
856          try {
857            this.selectedProject2.setStartDate(Tstamp.makeTimestamp(startDateString)
858                .toGregorianCalendar().getTimeInMillis());
859          }
860          catch (Exception e) {
861            isLoadSucceed = false;
862            String error = "Errors when parsing start date from URL parameter: " + startDateString;
863            logger.warning(error + R_HAND + e.getMessage());
864            errorMessage.append(error);
865            errorMessage.append('\n');
866          }
867        }
868        else {
869          isLoadSucceed = false;
870          errorMessage.append("startDate key is missing in URL parameters.");
871        }
872        // load end date
873        if (parameters.containsKey(END_DATE2_KEY)) {
874          String endDateString = parameters.getString(END_DATE2_KEY);
875          try {
876            this.selectedProject2.setEndDate(Tstamp.makeTimestamp(endDateString).toGregorianCalendar()
877                .getTimeInMillis());
878          }
879          catch (Exception e) {
880            isLoadSucceed = false;
881            String error = "Errors when parsing end date from URL parameter: " + endDateString;
882            logger.warning(error + R_HAND + e.getMessage());
883            errorMessage.append(error);
884            errorMessage.append('\n');
885          }
886        }
887        else {
888          isLoadSucceed = false;
889          errorMessage.append("endDate key is missing in URL parameters.\n");
890        }
891    
892        // load telemetry name
893        if (parameters.containsKey(TELEMETRY_KEY)) {
894          String telemetryString = parameters.getString(TELEMETRY_KEY);
895          if (this.getTelemetryList().contains(telemetryString)
896              && telemetryString.equalsIgnoreCase("Build")) {
897            this.setTelemetryName(telemetryString);
898            isTelemetryLoaded = true;
899          }
900          else {
901            isLoadSucceed = false;
902            String error = "Telemetry from URL parameter is unknown: " + telemetryString;
903            logger.warning(error);
904            errorMessage.append(error);
905            errorMessage.append('\n');
906          }
907        }
908        else {
909          isLoadSucceed = false;
910          errorMessage.append("Telemetry key is missing in URL parameters.\n");
911        }
912        // load granularity
913        if (parameters.containsKey(GRANULARITY_KEY)) {
914          String granularityString = parameters.getString(GRANULARITY_KEY);
915          if (this.granularityList.contains(granularityString)) {
916            this.setGranularity(granularity);
917          }
918          else {
919            isLoadSucceed = false;
920            String error = "Granularity is not supported: " + granularityString;
921            logger.warning(error);
922            errorMessage.append(error);
923            errorMessage.append('\n');
924          }
925        }
926        else {
927          isLoadSucceed = false;
928          errorMessage.append("granularity key is missing in URL parameters.\n");
929        }
930        // load telemetry parameters
931        if (parameters.containsKey(TELEMETRY_PARAMERTERS_KEY)) {
932          String paramString = parameters.getString(TELEMETRY_PARAMERTERS_KEY);
933          String[] paramStringArray = paramString.split(PARAMETER_VALUE_SEPARATOR);
934          this.parameters.clear();
935          if (isTelemetryLoaded) {
936            List<ParameterDefinition> paramDefList = this.getTelemetrys().get(this.telemetryName)
937                .getParameterDefinition();
938            if (paramStringArray.length == paramDefList.size()) {
939              for (int i = 0; i < paramStringArray.length; ++i) {
940                if (isValueMatchType(paramStringArray[i], paramDefList.get(i).getType())) {
941                  this.parameters.add(new Model(paramStringArray[i]));
942                }
943                else {
944                  isLoadSucceed = false;
945                  String error = "Telemetry parameter: " + paramStringArray[i] + " is not matched to"
946                      + " type: " + paramDefList.get(i).getType().getName();
947                  logger.warning(error);
948                  errorMessage.append(error);
949                  errorMessage.append('\n');
950                }
951              }
952            }
953            else {
954              isLoadSucceed = false;
955              String error = "Error in URL parameters: telemetry parameters: "
956                  + parameters.getString(TELEMETRY_PARAMERTERS_KEY) + "(" + paramStringArray.length
957                  + ") >> number of parameters not matched to definition within telemetry: "
958                  + this.telemetryName + "(" + paramDefList.size() + ")";
959              logger.warning(error);
960              errorMessage.append(error);
961              errorMessage.append('\n');
962            }
963          }
964        }
965        else {
966          isLoadSucceed = false;
967          errorMessage.append("param key is missing in URL parameters.\n");
968        }
969        if (errorMessage.length() > 0) {
970          this.paramErrorMessage = errorMessage.toString();
971        }
972        return isLoadSucceed;
973      }
974    
975      /**
976       * Checks if the given value is of the given type.
977       * 
978       * @param value the given value.
979       * @param type the given type.
980       * @return true if the value and type are matched.
981       */
982      private boolean isValueMatchType(String value, Type type) {
983        if ("Enumerated".equals(type.getName())) {
984          return type.getValue().contains(value);
985        }
986        else if ("Boolean".equals(type.getName())) {
987          return "true".equals(value) || "false".equals(value);
988        }
989        else if ("Integer".equals(type.getName())) {
990          try {
991            Integer.valueOf(value);
992            return true;
993          }
994          catch (NumberFormatException e) {
995            return false;
996          }
997        }
998        else if ("Text".equals(type.getName())) {
999          return true;
1000        }
1001        return false;
1002      }
1003    
1004      /**
1005       * Returns a PageParameters instance that represents the content of the input form.
1006       * 
1007       * @return a PageParameters instance.
1008       */
1009      public PageParameters getPageParameters() {
1010        PageParameters parameters = new PageParameters();
1011    
1012        parameters.put(SELECTED_PROJECT1_KEY, this.getSelectedProject1AsString());
1013        parameters.put(SELECTED_PROJECT1_KEY, this.getSelectedProject2AsString());
1014        parameters.put(START_DATE1_KEY, this.getFormattedProject1StartDateString());
1015        parameters.put(END_DATE1_KEY, this.getFormattedProject1EndDateString());
1016        parameters.put(START_DATE2_KEY, this.getFormattedProject2StartDateString());
1017        parameters.put(END_DATE2_KEY, this.getFormattedProject2EndDateString());
1018        parameters.put(TELEMETRY_KEY, this.getTelemetryName());
1019        parameters.put(GRANULARITY_KEY, this.getGranularity());
1020        parameters.put(TELEMETRY_PARAMERTERS_KEY, this.getParametersAsString());
1021        return parameters;
1022      }
1023    
1024      /**
1025       * @return the paramErrorMessage
1026       */
1027      public String getParamErrorMessage() {
1028        String temp = this.paramErrorMessage;
1029        this.clearParamErrorMessage();
1030        return temp;
1031      }
1032    
1033      /**
1034       * Clears the paramErrorMessage.
1035       */
1036      public void clearParamErrorMessage() {
1037        this.paramErrorMessage = "";
1038      }
1039    
1040      /**
1041       * Helps to format parameters list.
1042       * 
1043       * @param parameters The parameters list.
1044       * @return formatted parameters log message.
1045       */
1046      private String parametersToLog(List<IModel> parameters) {
1047        StringBuffer sb = new StringBuffer(500);
1048        for (IModel im : parameters) {
1049          sb.append(IDNT + IDNT2 + im.getClass() + ": " + im.getObject() + CR);
1050        }
1051        return sb.toString();
1052      }
1053    
1054      /**
1055       * {@inheritDoc}
1056       */
1057      public String toString() {
1058        StringBuffer sb = new StringBuffer(1024);
1059        sb.append("TrajectorySession: " + CR);
1060        sb.append(IDNT + "project1: " + getSelectedProject1AsString() + CR + IDNT
1061            + getFormattedProject1StartDateString() + SP + getFormattedProject1EndDateString() + CR);
1062        sb.append(IDNT + "project2 " + getSelectedProject2AsString() + CR + IDNT
1063            + getFormattedProject2StartDateString() + SP + getFormattedProject2EndDateString() + CR);
1064        sb.append(IDNT + "hash code: " + this.hashCode());
1065        return sb.toString();
1066      }
1067    
1068    }