1 /*
   2  * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 package issuetrackingbidi.model;
  33 
  34 import issuetrackingbidi.model.Issue.IssueStatus;
  35 
  36 import java.time.LocalDate;
  37 import java.util.Arrays;
  38 import java.util.List;
  39 import java.util.Map;
  40 import java.util.TreeMap;
  41 import java.util.concurrent.atomic.AtomicInteger;
  42 import javafx.beans.property.SimpleObjectProperty;
  43 import javafx.beans.property.SimpleStringProperty;
  44 import javafx.beans.value.ObservableValue;
  45 import javafx.collections.FXCollections;
  46 import javafx.collections.MapChangeListener;
  47 import javafx.collections.ObservableList;
  48 import javafx.collections.ObservableMap;
  49 
  50 public class TrackingServiceStub implements TrackingService {
  51 
  52     // You add a project by adding an entry with an empty observable array list
  53     // of issue IDs in the projects Map.
  54     final ObservableMap<String, ObservableList<String>> projectsMap;
  55     {
  56         final Map<String, ObservableList<String>> map = new TreeMap<>();
  57         projectsMap = FXCollections.observableMap(map);
  58         for (String s : newList("Project1", "Project2", "Project3", "Project4")) {
  59             projectsMap.put(s, FXCollections.<String>observableArrayList());
  60         }
  61     }
  62 
  63     // The projectNames list is kept in sync with the project's map by observing
  64     // the projectsMap and modifying the projectNames list in consequence.
  65     final ObservableList<String> projectNames = FXCollections.<String>observableArrayList();
  66     final MapChangeListener<String, ObservableList<String>> projectsMapChangeListener = change -> {
  67         if (change.wasAdded()) projectNames.add(change.getKey());
  68         if (change.wasRemoved()) projectNames.remove(change.getKey());
  69     };
  70 
  71     {
  72         projectNames.addAll(projectsMap.keySet());
  73         projectsMap.addListener(projectsMapChangeListener);
  74     }
  75 
  76     // A Issue stub.
  77     public final class IssueStub implements ObservableIssue {
  78         private final SimpleStringProperty id;
  79         private final SimpleStringProperty projectName;
  80         private final SimpleStringProperty title;
  81         private final SimpleStringProperty description;
  82         private final SimpleObjectProperty<IssueStatus> status =
  83                 new SimpleObjectProperty<>(IssueStatus.NEW);
  84         private final SimpleObjectProperty<LocalDate> neededBy =
  85                 new SimpleObjectProperty<>(LocalDate.now());
  86 
  87         IssueStub(String projectName, String id) {
  88             this(projectName, id, null);
  89         }
  90 
  91         IssueStub(String projectName, String id, String title) {
  92             assert projectNames.contains(projectName);
  93             assert ! projectsMap.get(projectName).contains(id);
  94             assert ! issuesMap.containsKey(id);
  95             this.projectName = new SimpleStringProperty(projectName);
  96             this.id = new SimpleStringProperty(id);
  97             this.title = new SimpleStringProperty(title);
  98             this.description = new SimpleStringProperty("");
  99         }
 100 
 101         @Override
 102         public IssueStatus getStatus() {
 103             return status.get();
 104         }
 105 
 106         private void setStatus(IssueStatus issueStatus) {
 107             this.status.set(issueStatus);
 108         }
 109 
 110         @Override
 111         public String getId() {
 112             return id.get();
 113         }
 114 
 115         @Override
 116         public String getProjectName() {
 117             return projectName.get();
 118         }
 119 
 120         @Override
 121         public String getSynopsis() {
 122             return title.get();
 123         }
 124 
 125         private void setSynopsis(String title) {
 126             this.title.set(title);
 127         }
 128 
 129         @Override
 130         public String getDescription() {
 131             return description.get();
 132         }
 133 
 134         private void setDescription(String description) {
 135             this.description.set(description);
 136         }
 137 
 138         @Override
 139         public LocalDate getNeededBy() {
 140             return neededBy.get();
 141         }
 142 
 143         private void setNeededBy(LocalDate date) {
 144             this.neededBy.set(date);
 145         }
 146 
 147         @Override
 148         public ObservableValue<String> idProperty() {
 149             return id;
 150         }
 151 
 152         @Override
 153         public ObservableValue<String> projectNameProperty() {
 154             return projectName;
 155         }
 156 
 157         @Override
 158         public ObservableValue<IssueStatus> statusProperty() {
 159             return status;
 160         }
 161 
 162         @Override
 163         public ObservableValue<String> synopsisProperty() {
 164             return title;
 165         }
 166 
 167         @Override
 168         public ObservableValue<String> descriptionProperty() {
 169             return description;
 170         }
 171 
 172         @Override
 173         public ObservableValue<LocalDate> neededByProperty() {
 174             return neededBy;
 175         }
 176     }
 177 
 178     // You create new issue by adding a IssueStub instance to the issuesMap.
 179     // the new id will be automatically added to the corresponding list in
 180     // the projectsMap.
 181     //
 182     final MapChangeListener<String, IssueStub> issuesMapChangeListener = change -> {
 183         if (change.wasAdded()) {
 184             final IssueStub val1 = change.getValueAdded();
 185             projectsMap.get(val1.getProjectName()).add(val1.getId());
 186         }
 187         if (change.wasRemoved()) {
 188             final IssueStub val2 = change.getValueRemoved();
 189             projectsMap.get(val2.getProjectName()).remove(val2.getId());
 190         }
 191     };
 192 
 193     final AtomicInteger issueCounter = new AtomicInteger(0);
 194     final ObservableMap<String, IssueStub> issuesMap;
 195     {
 196         final Map<String, IssueStub> map = new TreeMap<>();
 197         issuesMap = FXCollections.observableMap(map);
 198         issuesMap.addListener(issuesMapChangeListener);
 199         IssueStub ts;
 200         ts = createIssueFor("Project1");
 201         ts.setSynopsis("We rode in sorrow, with strong hounds three");
 202         ts.setDescription("From \"The Wanderings Of Oisin\".\nW. B. Yeats.");
 203         ts.setNeededBy(LocalDate.now().plusDays(3));
 204         ts = createIssueFor("Project1");
 205         ts.setSynopsis("Bran, Sgeolan, and Lomair");
 206         ts.setDescription("From \"The Wanderings Of Oisin\".\nW. B. Yeats.");
 207         ts.setNeededBy(LocalDate.now().plusWeeks(3));
 208         ts = createIssueFor("Project2");
 209         ts.setSynopsis("On a morning misty and mild and fair");
 210         ts.setDescription("From \"The Wanderings Of Oisin\".\nW. B. Yeats.");
 211         ts.setNeededBy(LocalDate.now().plusDays(3));
 212         ts = createIssueFor("Project4");
 213         ts.setSynopsis("The mist-drops hung on the fragrant trees");
 214         ts.setDescription("From \"The Wanderings Of Oisin\".\nW. B. Yeats.");
 215         ts.setNeededBy(LocalDate.now().plusDays(3));
 216         ts = createIssueFor("Project3");
 217         ts.setSynopsis("And in the blossoms hung the bees");
 218         ts.setDescription("From \"The Wanderings Of Oisin\".\nW. B. Yeats.");
 219         ts.setNeededBy(LocalDate.now().plusDays(3));
 220         ts = createIssueFor("Project2");
 221         ts.setSynopsis("We rode in sadness above Lough Lean");
 222         ts.setDescription("From \"The Wanderings Of Oisin\".\nW. B. Yeats.");
 223         ts.setNeededBy(LocalDate.now().plusMonths(3));
 224         ts = createIssueFor("Project1");
 225         ts.setSynopsis("For our best were dead on Gavra's green");
 226         ts.setDescription("From \"The Wanderings Of Oisin\".\nW. B. Yeats.");
 227         ts.setNeededBy(LocalDate.now().plusMonths(3));
 228         ts = createIssueFor("Project4");
 229         ts.setSynopsis("The Wanderings of Oisin");
 230         ts.setDescription("William Butler Yeats.");
 231         ts.setNeededBy(LocalDate.now().plusWeeks(3));
 232     }
 233 
 234     @SafeVarargs
 235     private static <T> List<T> newList(T... items) {
 236         return Arrays.asList(items);
 237     }
 238 
 239 
 240     @Override
 241     public IssueStub createIssueFor(String projectName) {
 242         assert projectNames.contains(projectName);
 243         final IssueStub issue = new IssueStub(projectName, "TT-"+issueCounter.incrementAndGet());
 244         assert issuesMap.containsKey(issue.getId()) == false;
 245         assert projectsMap.get(projectName).contains(issue.getId()) == false;
 246         issuesMap.put(issue.getId(), issue);
 247         return issue;
 248     }
 249 
 250     @Override
 251     public void deleteIssue(String issueId) {
 252         assert issuesMap.containsKey(issueId);
 253         issuesMap.remove(issueId);
 254     }
 255 
 256     @Override
 257     public ObservableList<String> getProjectNames() {
 258         return projectNames;
 259     }
 260 
 261     @Override
 262     public ObservableList<String> getIssueIds(String projectName) {
 263         return projectsMap.get(projectName);
 264     }
 265 
 266     @Override
 267     public IssueStub getIssue(String issueId) {
 268         return issuesMap.get(issueId);
 269     }
 270 
 271     @Override
 272     public void saveIssue(String issueId, IssueStatus status,
 273             String synopsis, String description, LocalDate updated) {
 274         IssueStub issue = getIssue(issueId);
 275         issue.setDescription(description);
 276         issue.setSynopsis(synopsis);
 277         issue.setStatus(status);
 278         issue.setNeededBy(updated);
 279     }
 280 
 281 }