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 }