001: /*
002: * Copyright (C) Jakub Neubauer, 2007
003: *
004: * This file is part of TaskBlocks
005: *
006: * TaskBlocks is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 3 of the License, or
009: * (at your option) any later version.
010: *
011: * TaskBlocks is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program. If not, see <http://www.gnu.org/licenses/>.
018: */
019:
020: package taskblocks.io;
021:
022: import java.io.File;
023: import java.io.IOException;
024: import java.util.ArrayList;
025: import java.util.HashMap;
026: import java.util.HashSet;
027: import java.util.List;
028: import java.util.Map;
029: import java.util.Set;
030:
031: import javax.xml.parsers.DocumentBuilderFactory;
032: import javax.xml.parsers.ParserConfigurationException;
033: import javax.xml.transform.Transformer;
034: import javax.xml.transform.TransformerException;
035: import javax.xml.transform.TransformerFactory;
036: import javax.xml.transform.dom.DOMSource;
037: import javax.xml.transform.stream.StreamResult;
038:
039: import org.w3c.dom.Document;
040: import org.w3c.dom.Element;
041: import org.w3c.dom.Node;
042: import org.w3c.dom.NodeList;
043: import org.xml.sax.SAXException;
044:
045: import taskblocks.Pair;
046: import taskblocks.Utils;
047: import taskblocks.modelimpl.ColorLabel;
048: import taskblocks.modelimpl.ManImpl;
049: import taskblocks.modelimpl.TaskImpl;
050: import taskblocks.modelimpl.TaskModelImpl;
051:
052: /**
053: * Used to load/save the task project model
054: *
055: * @author jakub
056: *
057: */
058: public class ProjectSaveLoad {
059:
060: public static final String TASKMAN_E = "taskman";
061: public static final String TASKS_E = "tasks";
062: public static final String MANS_E = "mans";
063: public static final String TASK_E = "task";
064: public static final String MAN_E = "man";
065: public static final String PREDECESSORS_E = "predecessors";
066: public static final String PREDECESSOR_E = "predecessor";
067:
068: public static final String NAME_A = "name";
069: public static final String ID_A = "id";
070: public static final String START_A = "start";
071: public static final String DURATION_A = "duration";
072: public static final String MAN_A = "man";
073: public static final String PRED_A = "pred";
074: public static final String COLOR_A = "color";
075:
076: TaskModelImpl _model;
077: Map<String, TaskImpl> _taskIds;
078: Map<String, ManImpl> _manIds;
079:
080: /**
081: * Loads project data model from given file.
082: * TODO: checks for missing data in elements
083: *
084: * @param f
085: * @return
086: * @throws WrongDataException
087: */
088: public TaskModelImpl loadProject(File f) throws WrongDataException {
089: try {
090: DocumentBuilderFactory dbf = DocumentBuilderFactory
091: .newInstance();
092: Document doc;
093: doc = dbf.newDocumentBuilder().parse(f);
094: Element rootE = doc.getDocumentElement();
095: if (!TASKMAN_E.equals(rootE.getNodeName())) {
096: throw new WrongDataException(
097: "Document is not TaskManager project");
098: }
099:
100: // mapping ID -> ManImpl
101: Map<String, ManImpl> mans = new HashMap<String, ManImpl>();
102: // mapping ID -> TaskImpl
103: Map<String, TaskImpl> tasks = new HashMap<String, TaskImpl>();
104: // mapping tasks -> list of their predecessors IDs
105: List<Pair<TaskImpl, String[]>> taskPredecessorsIds = new ArrayList<Pair<TaskImpl, String[]>>();
106:
107: // 1. load all tasks and mans alone, 2. bind them between each other
108: Element mansE = getFirstChild(rootE, MANS_E);
109: if (mansE != null) {
110: Element[] manEs = Utils.getChilds(mansE, MAN_E);
111: for (Element manE : manEs) {
112: String manName = manE.getAttribute(NAME_A);
113: String manId = manE.getAttribute(ID_A);
114: ManImpl man = new ManImpl();
115: man.setName(manName);
116: mans.put(manId, man);
117: }
118: }
119: Element tasksE = getFirstChild(rootE, TASKS_E);
120: if (tasksE != null) {
121: for (Element taskE : Utils.getChilds(tasksE, TASK_E)) {
122: String taskId = taskE.getAttribute(ID_A);
123: String taskName = taskE.getAttribute(NAME_A);
124: long taskStart = Long.valueOf(taskE
125: .getAttribute(START_A));
126: long taskDuration = Long.valueOf(taskE
127: .getAttribute(DURATION_A));
128: String colorTxt = taskE.getAttribute(COLOR_A);
129: String taskManId = taskE.getAttribute(MAN_A);
130: ManImpl man = mans.get(taskManId); // mans are already loaded
131:
132: if (man == null) {
133: throw new WrongDataException("Task with id "
134: + taskId
135: + " is not assigned to any man");
136: }
137:
138: TaskImpl task = new TaskImpl();
139: task.setName(taskName);
140: task.setStartTime(taskStart);
141: task.setDuration(taskDuration);
142: task.setMan(man);
143: if (colorTxt != null && colorTxt.length() > 0) {
144: int colorIndex = Integer.parseInt(colorTxt);
145: if (colorIndex >= 0
146: && colorIndex < ColorLabel.COLOR_LABELS.length) {
147: task
148: .setColorLabel(ColorLabel.COLOR_LABELS[colorIndex]);
149: }
150: }
151:
152: // read predecessors ids
153: Element predsE = getFirstChild(taskE,
154: PREDECESSORS_E);
155: if (predsE != null) {
156: List<String> preds = new ArrayList<String>();
157: for (Element predE : Utils.getChilds(predsE,
158: PREDECESSOR_E)) {
159: preds.add(predE.getAttribute(PRED_A));
160: }
161: taskPredecessorsIds
162: .add(new Pair<TaskImpl, String[]>(task,
163: preds.toArray(new String[preds
164: .size()])));
165: }
166:
167: tasks.put(taskId, task);
168: }
169: }
170:
171: // now count the predecessors of tasks
172: for (Pair<TaskImpl, String[]> taskAndPredIds : taskPredecessorsIds) {
173: List<TaskImpl> preds = new ArrayList<TaskImpl>();
174: for (String predId : taskAndPredIds.snd) {
175: TaskImpl pred = tasks.get(predId);
176: if (pred == null) {
177: System.out
178: .println("Warning: Task predecessor with id "
179: + predId + " doesn't exist");
180: } else if (pred == taskAndPredIds.fst) {
181: System.out.println("Warning: Task with id "
182: + predId + " is it's own predecessor");
183: } else {
184: preds.add(pred);
185: }
186: }
187: taskAndPredIds.fst.setPredecessors(preds
188: .toArray(new TaskImpl[preds.size()]));
189: }
190:
191: TaskModelImpl taskModel = new TaskModelImpl(tasks.values()
192: .toArray(new TaskImpl[tasks.size()]), mans.values()
193: .toArray(new ManImpl[mans.size()]));
194:
195: return taskModel;
196:
197: } catch (SAXException e) {
198: throw new WrongDataException(
199: "Document is not valid data file", e);
200: } catch (IOException e) {
201: throw new WrongDataException("Can't read file: "
202: + e.getMessage(), e);
203: } catch (ParserConfigurationException e) {
204: throw new WrongDataException(
205: "Document is not TaskManager project", e);
206: }
207: }
208:
209: /**
210: * Saves project to specified file
211: *
212: * @param f
213: * @param model
214: * @throws TransformerException
215: * @throws ParserConfigurationException
216: */
217: public void saveProject(File f, TaskModelImpl model)
218: throws TransformerException, ParserConfigurationException {
219: DocumentBuilderFactory dbf = DocumentBuilderFactory
220: .newInstance();
221: Document doc;
222: doc = dbf.newDocumentBuilder().newDocument();
223:
224: _model = model;
225: _taskIds = new HashMap<String, TaskImpl>();
226: _manIds = new HashMap<String, ManImpl>();
227:
228: // build the xml tree
229: saveProject(doc);
230: prettyLayout((Element) doc.getFirstChild());
231:
232: TransformerFactory tf = TransformerFactory.newInstance();
233: Transformer t = tf.newTransformer();
234: t.transform(new DOMSource(doc), new StreamResult(f));
235: }
236:
237: private void saveProject(Document doc) {
238: Element rootE = doc.createElement(TASKMAN_E);
239: doc.appendChild(rootE);
240: Set<ManImpl> mans = new HashSet<ManImpl>();
241:
242: // generate list of mans
243: for (TaskImpl t : _model._tasks) {
244: mans.add(t.getMan());
245: }
246:
247: // generate task and man ids
248: int lastTaskId = 1;
249: int lastManId = 1;
250: for (TaskImpl t : _model._tasks) {
251: t._id = String.valueOf(lastTaskId++);
252: }
253: for (ManImpl man : mans) {
254: man._id = String.valueOf(lastManId++);
255: }
256:
257: // save mans
258: Element mansE = doc.createElement(MANS_E);
259: for (ManImpl man : mans) {
260: saveMan(mansE, man);
261: }
262: rootE.appendChild(mansE);
263:
264: // save tasks
265: Element tasksE = doc.createElement(TASKS_E);
266: for (TaskImpl t : _model._tasks) {
267: saveTask(tasksE, t);
268: }
269: rootE.appendChild(tasksE);
270: }
271:
272: private void saveMan(Element mansE, ManImpl man) {
273: Element manE = mansE.getOwnerDocument().createElement(MAN_E);
274: manE.setAttribute(ID_A, man._id);
275: manE.setAttribute(NAME_A, man.getName());
276: mansE.appendChild(manE);
277: }
278:
279: private void saveTask(Element tasksE, TaskImpl t) {
280: Element taskE = tasksE.getOwnerDocument().createElement(TASK_E);
281: taskE.setAttribute(NAME_A, t.getName());
282: taskE.setAttribute(ID_A, t._id);
283: taskE.setAttribute(START_A, String.valueOf(t.getStartTime()));
284: taskE.setAttribute(DURATION_A, String.valueOf(t.getDuration()));
285: taskE.setAttribute(MAN_A, t.getMan()._id);
286: if (t.getColorLabel() != null) {
287: taskE.setAttribute(COLOR_A, String.valueOf(t
288: .getColorLabel()._index));
289: }
290:
291: // save predecessors
292: if (t.getPredecessors().length > 0) {
293: Element predsE = taskE.getOwnerDocument().createElement(
294: PREDECESSORS_E);
295: for (TaskImpl pred : t.getPredecessors()) {
296: Element predE = predsE.getOwnerDocument()
297: .createElement(PREDECESSOR_E);
298: predE.setAttribute(PRED_A, pred._id);
299: predsE.appendChild(predE);
300: }
301: taskE.appendChild(predsE);
302: }
303: tasksE.appendChild(taskE);
304: }
305:
306: private Element getFirstChild(Element e, String name) {
307: NodeList nl = e.getChildNodes();
308: for (int i = 0; i < nl.getLength(); i++) {
309: Node n = nl.item(i);
310: if (n.getNodeType() == Node.ELEMENT_NODE
311: && name.equals(n.getNodeName())) {
312: return (Element) n;
313: }
314: }
315: return null;
316: }
317:
318: private void prettyLayout(Element e) {
319: prettyLayoutRec(e, "");
320: }
321:
322: private void prettyLayoutRec(Element e, String currentIndent) {
323:
324: // insert spaces before 'e'
325: // but only if indent > 0. This also resolves problem that we cannot insert
326: // anything in Document node (before root element).
327: if (currentIndent.length() > 0) {
328: e.getParentNode().insertBefore(
329: e.getOwnerDocument().createTextNode(currentIndent),
330: e);
331: }
332:
333: // first check if element has some sub-element. if true, prettyLayout them
334: // recursively with increase indent
335: NodeList nl = e.getChildNodes();
336: boolean hasChildrenElems = false;
337: for (int i = 0; i < nl.getLength(); i++) {
338: Node n = nl.item(i);
339: if (n.getNodeType() == Node.ELEMENT_NODE) {
340: hasChildrenElems = true;
341: break;
342: }
343: }
344:
345: if (hasChildrenElems) {
346: // \n after start-tag. it means before first child
347: e.insertBefore(e.getOwnerDocument().createTextNode("\n"), e
348: .getFirstChild());
349:
350: // indent before end-tag. It means just as last child
351: e.appendChild(e.getOwnerDocument().createTextNode(
352: currentIndent));
353:
354: // recursively indent children
355: currentIndent += " ";
356: // we must get the nodelist again, because previous adding of childs broked
357: // the old nodelist.
358: Node n = e.getFirstChild();
359: while (n != null) {
360: if (n.getNodeType() == Node.ELEMENT_NODE) {
361: prettyLayoutRec((Element) n, currentIndent);
362: }
363: n = n.getNextSibling();
364: }
365: }
366:
367: // \n after end-tag
368: Node text = e.getOwnerDocument().createTextNode("\n");
369: if (e.getNextSibling() == null) {
370: if (e.getParentNode().getNodeType() != Node.DOCUMENT_NODE) {
371: e.getParentNode().appendChild(text);
372: }
373: } else {
374: e.getParentNode().insertBefore(text, e.getNextSibling());
375: }
376: }
377: }
|