001: /*--------------------------------------------------------------------------*
002: | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann |
003: | |
004: | This program is free software; you can redistribute it and/or modify |
005: | it under the terms of the GNU General Public License as published by the |
006: | Free Software Foundation. A copy of the license has been included with |
007: | these distribution in the COPYING file, if not go to www.fsf.org |
008: | |
009: | As a special exception, you are granted the permissions to link this |
010: | program with every library, which license fulfills the Open Source |
011: | Definition as published by the Open Source Initiative (OSI). |
012: *--------------------------------------------------------------------------*/
013: package org.rapla.gui.internal.view;
014:
015: import java.awt.Component;
016: import java.awt.Font;
017: import java.util.ArrayList;
018: import java.util.Arrays;
019: import java.util.Collection;
020: import java.util.Comparator;
021: import java.util.HashMap;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Locale;
025: import java.util.Map;
026: import java.util.Set;
027: import java.util.TreeSet;
028:
029: import javax.swing.BorderFactory;
030: import javax.swing.Icon;
031: import javax.swing.JTree;
032: import javax.swing.UIManager;
033: import javax.swing.border.Border;
034: import javax.swing.tree.DefaultMutableTreeNode;
035: import javax.swing.tree.DefaultTreeCellRenderer;
036: import javax.swing.tree.DefaultTreeModel;
037: import javax.swing.tree.MutableTreeNode;
038: import javax.swing.tree.TreeCellRenderer;
039: import javax.swing.tree.TreeModel;
040: import javax.swing.tree.TreeNode;
041:
042: import org.rapla.components.util.Assert;
043: import org.rapla.components.util.InverseComparator;
044: import org.rapla.entities.Category;
045: import org.rapla.entities.Named;
046: import org.rapla.entities.NamedComparator;
047: import org.rapla.entities.RaplaType;
048: import org.rapla.entities.User;
049: import org.rapla.entities.domain.Allocatable;
050: import org.rapla.entities.domain.Period;
051: import org.rapla.entities.domain.Reservation;
052: import org.rapla.entities.domain.ReservationStartComparator;
053: import org.rapla.entities.dynamictype.Classifiable;
054: import org.rapla.entities.dynamictype.Classification;
055: import org.rapla.entities.dynamictype.ClassificationFilter;
056: import org.rapla.entities.dynamictype.DynamicType;
057: import org.rapla.entities.dynamictype.DynamicTypeAnnotations;
058: import org.rapla.facade.Conflict;
059: import org.rapla.framework.RaplaContext;
060: import org.rapla.framework.RaplaException;
061: import org.rapla.gui.RaplaGUIComponent;
062: import org.rapla.gui.TreeFactory;
063: import org.rapla.gui.internal.common.CalendarSelectionModel;
064: import org.rapla.gui.toolkit.RecursiveNode;
065: import org.rapla.gui.toolkit.TreeToolTipRenderer;
066:
067: public class TreeFactoryImpl extends RaplaGUIComponent implements
068: TreeFactory {
069: public TreeFactoryImpl(RaplaContext sm) throws RaplaException {
070: super (sm);
071: this .defaultIcon = getIcon("icon.tree.default");
072: }
073:
074: Icon defaultIcon;
075:
076: public TreeModel createClassifiableModel(
077: Classifiable[] classifiables) throws RaplaException {
078: return createClassifiableModel(classifiables, null);
079: }
080:
081: public DefaultTreeModel createClassifiableModel(
082: Classifiable[] classifiables, DynamicType[] dynamicTypes)
083: throws RaplaException {
084: boolean addOnlyTypesWithVisibleChildren = false;
085: if (dynamicTypes == null || dynamicTypes.length == 0) {
086: dynamicTypes = getQuery().getDynamicTypes(null);
087: addOnlyTypesWithVisibleChildren = true;
088: }
089: Map nodeMap = new HashMap();
090:
091: for (int i = 0; i < dynamicTypes.length; i++) {
092: TreeNode node = new NamedNode(dynamicTypes[i]);
093: nodeMap.put(dynamicTypes[i], node);
094: }
095:
096: DefaultMutableTreeNode root = new DefaultMutableTreeNode("ROOT");
097: Comparator comp = null;
098: if (classifiables.length > 0
099: && classifiables[0] instanceof Reservation) {
100: comp = new InverseComparator(
101: new ReservationStartComparator(getLocale()));
102: } else {
103: comp = new NamedComparator(getLocale());
104: }
105: Set sortedClassifiable = new TreeSet(comp);
106: sortedClassifiable.addAll(Arrays.asList(classifiables));
107: for (Iterator it = sortedClassifiable.iterator(); it.hasNext();) {
108: Classifiable classifiable = (Classifiable) it.next();
109: Classification classification = classifiable
110: .getClassification();
111: NamedNode childNode = new NamedNode((Named) classifiable);
112: DynamicType type = classification.getType();
113: Assert.notNull(type);
114: DefaultMutableTreeNode typeNode = (DefaultMutableTreeNode) nodeMap
115: .get(type);
116: Assert.notNull(typeNode);
117: typeNode.add(childNode);
118:
119: }
120:
121: for (int i = 0, count = 0; i < dynamicTypes.length; i++) {
122: DefaultMutableTreeNode typeNode = (DefaultMutableTreeNode) nodeMap
123: .get(dynamicTypes[i]);
124: // Only add typeNodes with children
125: if (!typeNode.isLeaf() || !addOnlyTypesWithVisibleChildren)
126: root.insert(typeNode, count++);
127: }
128: return new DefaultTreeModel(root);
129: }
130:
131: public TreeCellRenderer createComplexRenderer() {
132: return new ComplexTreeCellRenderer();
133: }
134:
135: private boolean isInFilter(ClassificationFilter[] filter,
136: Classifiable classifiable) {
137: if (filter.length == 0)
138: return true;
139: for (int i = 0; i < filter.length; i++) {
140: if (filter[i].matches(classifiable.getClassification())) {
141: return true;
142: }
143: }
144: return false;
145: }
146:
147: private boolean isInFilter(ClassificationFilter[] filter,
148: DynamicType type) {
149: if (filter.length == 0)
150: return true;
151: for (int i = 0; i < filter.length; i++) {
152: if (filter[i].getType().equals(type)) {
153: return true;
154: }
155: }
156: return false;
157: }
158:
159: private boolean hasRulesFor(ClassificationFilter[] filter,
160: DynamicType type) {
161: if (filter.length == 0)
162: return false;
163: for (int i = 0; i < filter.length; i++) {
164: if (filter[i].getType().equals(type)
165: && filter[i].ruleSize() > 0) {
166: return true;
167: }
168: }
169: return false;
170: }
171:
172: private Collection getConflicts(ClassificationFilter[] filter,
173: User user) throws RaplaException {
174:
175: List result = new ArrayList();
176: Conflict[] conflicts = getQuery().getConflicts(
177: getQuery().today());
178: for (int i = 0; i < conflicts.length; i++) {
179: Conflict conflict = conflicts[i];
180: if (!isInFilter(filter, conflict.getAllocatable())) {
181: continue;
182: }
183: if (!isInFilter(filter, conflict.getReservation1())
184: && !isInFilter(filter, conflict.getReservation2())) {
185: continue;
186: }
187: if (user != null
188: && !user.equals(conflict.getReservation1()
189: .getOwner())
190: && !user.equals(conflict.getReservation1()
191: .getOwner())) {
192: continue;
193: }
194: result.add(conflict);
195: }
196: return result;
197: }
198:
199: public DefaultTreeModel createModel(ClassificationFilter[] filter,
200: User selectedUser) throws RaplaException {
201: // Resources and Persons
202: // Add the resource types
203: // Add the resources
204: // Add the person types
205: // Add the persons
206: // Eventtypes
207: // Add the event types
208: // Conflicts (0)
209: // Add the conflicts
210: // Users
211: // Add the users
212: // Categories (the root category)
213:
214: // Add the periods
215:
216: DefaultMutableTreeNode root = new DefaultMutableTreeNode("ROOT");
217: TypeNode resourceRoot = new TypeNode(Allocatable.TYPE,
218: CalendarSelectionModel.ALLOCATABLES_ROOT,
219: getString("resources"));
220:
221: TypeNode eventRoot = new TypeNode(Reservation.TYPE,
222: getString("reservations"));
223:
224: Map nodeMap = new HashMap();
225:
226: boolean resourcesFiltered = false;
227: boolean eventsFiltered = false;
228:
229: DynamicType[] types = getQuery().getDynamicTypes(
230: DynamicTypeAnnotations.VALUE_RESOURCE_CLASSIFICATION);
231: for (int i = 0; i < types.length; i++) {
232: DynamicType type = types[i];
233: if (hasRulesFor(filter, type)) {
234: resourcesFiltered = true;
235: }
236: if (!isInFilter(filter, type)) {
237: resourcesFiltered = true;
238: continue;
239: }
240:
241: NamedNode node = new NamedNode(type);
242: resourceRoot.add(node);
243: nodeMap.put(type, node);
244: }
245:
246: types = getQuery().getDynamicTypes(
247: DynamicTypeAnnotations.VALUE_PERSON_CLASSIFICATION);
248: for (int i = 0; i < types.length; i++) {
249: DynamicType type = types[i];
250: if (hasRulesFor(filter, type)) {
251: resourcesFiltered = true;
252: }
253: if (!isInFilter(filter, type)) {
254: resourcesFiltered = true;
255: continue;
256: }
257:
258: NamedNode node = new NamedNode(type);
259: resourceRoot.add(node);
260: nodeMap.put(type, node);
261: }
262:
263: types = getQuery()
264: .getDynamicTypes(
265: DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION);
266: for (int i = 0; i < types.length; i++) {
267: DynamicType type = types[i];
268: if (hasRulesFor(filter, type)) {
269: eventsFiltered = true;
270: }
271: if (!isInFilter(filter, type)) {
272: eventsFiltered = true;
273: continue;
274: }
275: NamedNode node = new NamedNode(type);
276: eventRoot.add(node);
277: }
278:
279: eventRoot.setFiltered(eventsFiltered);
280: resourceRoot.setFiltered(resourcesFiltered);
281:
282: Comparator comp = new NamedComparator(getLocale());
283: Set sortedClassifiable = new TreeSet(comp);
284: sortedClassifiable.addAll(Arrays.asList(getQuery()
285: .getAllocatables()));
286:
287: for (Iterator it = sortedClassifiable.iterator(); it.hasNext();) {
288: Classifiable classifiable = (Classifiable) it.next();
289: if (!isInFilter(filter, classifiable)) {
290: continue;
291: }
292: Classification classification = classifiable
293: .getClassification();
294: NamedNode childNode = new NamedNode((Named) classifiable);
295: DynamicType type = classification.getType();
296: Assert.notNull(type);
297: DefaultMutableTreeNode typeNode = (DefaultMutableTreeNode) nodeMap
298: .get(type);
299: Assert.notNull(typeNode);
300: typeNode.add(childNode);
301: }
302:
303: root.add(resourceRoot);
304: root.add(eventRoot);
305:
306: Collection conflicts = getConflicts(filter, selectedUser);
307:
308: if (conflicts.size() > 0) {
309: DefaultMutableTreeNode conflictRoot = new TypeNode(
310: Conflict.TYPE, getI18n().format("conflictUC",
311: new Integer(conflicts.size())));
312: for (Iterator it = conflicts.iterator(); it.hasNext();) {
313: conflictRoot.add(new NamedNode((Conflict) it.next()));
314: }
315: root.add(conflictRoot);
316: }
317:
318: if (isAdmin()) {
319: CategoryNode categoryRoot = new CategoryNode(getI18n()
320: .getLocale(), null, getQuery().getSuperCategory());
321: root.add(categoryRoot);
322:
323: DefaultMutableTreeNode userRoot = new TypeNode(User.TYPE,
324: getString("users"));
325: User[] userList = getQuery().getUsers();
326: for (int i = 0; i < userList.length; i++) {
327: NamedNode node = new NamedNode(userList[i]);
328: userRoot.add(node);
329: }
330: root.add(userRoot);
331:
332: DefaultMutableTreeNode periodRoot = new TypeNode(
333: Period.TYPE, getString("periods"));
334: Period[] periodList = getQuery().getPeriods();
335: for (int i = 0; i < periodList.length; i++) {
336: NamedNode node = new NamedNode(periodList[i]);
337: periodRoot.add(node);
338: }
339: root.add(periodRoot);
340: }
341:
342: //root.add( periods );
343: //root.add( categories );
344:
345: return new DefaultTreeModel(root);
346: }
347:
348: class TypeNode extends DefaultMutableTreeNode {
349: private static final long serialVersionUID = 1L;
350:
351: boolean filtered;
352: RaplaType type;
353: String title;
354:
355: TypeNode(RaplaType type, Object userObject, String title) {
356: this .type = type;
357: this .title = title;
358: setUserObject(userObject);
359: }
360:
361: TypeNode(RaplaType type, Object userObject) {
362: this (type, userObject, null);
363: }
364:
365: public RaplaType getType() {
366: return type;
367: }
368:
369: public boolean isFiltered() {
370: return filtered;
371: }
372:
373: public void setFiltered(boolean filtered) {
374: this .filtered = filtered;
375: }
376:
377: public Object getTitle() {
378: if (title != null) {
379: return title;
380: } else {
381: return userObject.toString();
382: }
383: }
384:
385: }
386:
387: public DefaultMutableTreeNode newNamedNode(Named element) {
388: return new NamedNode(element);
389: }
390:
391: public TreeModel createModel(Category category)
392: throws RaplaException {
393: return new DefaultTreeModel(new CategoryNode(getI18n()
394: .getLocale(), null, category));
395: }
396:
397: public TreeModel createModelFlat(Named[] element) {
398: DefaultMutableTreeNode root = new DefaultMutableTreeNode("");
399: for (int i = 0; i < element.length; i++) {
400: root.add(new NamedNode(element[i]));
401: }
402: return new DefaultTreeModel(root);
403: }
404:
405: public TreeToolTipRenderer createTreeToolTipRenderer() {
406: return new RaplaTreeToolTipRenderer();
407: }
408:
409: public TreeCellRenderer createRenderer() {
410: return new ComplexTreeCellRenderer();
411: }
412:
413: static public class CategoryNode extends RecursiveNode implements
414: MutableTreeNode {
415: Locale locale;
416:
417: public CategoryNode(Locale locale, TreeNode parent,
418: Category category) {
419: super (parent, category);
420: this .locale = locale;
421: }
422:
423: protected Category getCategory() {
424: return (Category) getUserObject();
425: }
426:
427: protected Object[] getChildObjects() {
428: return getCategory().getCategories();
429: }
430:
431: protected RecursiveNode createChildNode(Object userObject) {
432: return new CategoryNode(locale, this , (Category) userObject);
433: }
434:
435: public String toString() {
436: return getCategory().getName(locale);
437: }
438:
439: public void insert(MutableTreeNode child, int index) {
440: }
441:
442: public void remove(int index) {
443: }
444:
445: public void remove(MutableTreeNode node) {
446: }
447:
448: public void setUserObject(Object object) {
449: }
450:
451: public void removeFromParent() {
452: parent = null;
453: }
454:
455: public void setParent(MutableTreeNode newParent) {
456: parent = newParent;
457:
458: }
459: }
460:
461: public class NamedNode extends DefaultMutableTreeNode {
462: private static final long serialVersionUID = 1L;
463:
464: NamedNode(Named obj) {
465: super (obj);
466: }
467:
468: public String toString() {
469: Named obj = (Named) getUserObject();
470: if (obj != null) {
471: return obj.getName(getI18n().getLocale());
472: } else {
473: return super .toString();
474: }
475: }
476: };
477:
478: // TODO this class is a bit of Hack
479: class ComplexTreeCellRenderer extends DefaultTreeCellRenderer {
480: private static final long serialVersionUID = 1L;
481:
482: Icon personIcon;
483: Icon folderClosedIcon;
484: Icon folderOpenIcon;
485: Font normalFont;
486: Font bigFont;
487: Border nonIconBorder = BorderFactory.createEmptyBorder(1, 0, 1,
488: 0);
489: Border conflictBorder = BorderFactory.createEmptyBorder(2, 0,
490: 2, 0);
491:
492: public ComplexTreeCellRenderer() {
493: personIcon = TreeFactoryImpl.this
494: .getIcon("icon.tree.persons");
495: //folderClosedIcon = UIManager.getIcon("Tree.closedIcon");
496: //folderOpenIcon = UIManager.getIcon("Tree.openIcon");
497: folderClosedIcon = getI18n().getIcon("icon.folder");
498: folderOpenIcon = getI18n().getIcon("icon.folder");
499: normalFont = UIManager.getFont("Tree.font");
500: bigFont = normalFont.deriveFont(Font.BOLD,
501: (float) (normalFont.getSize() * 1.2));
502: setLeafIcon(defaultIcon);
503:
504: }
505:
506: public void setLeaf(Object object) {
507: Icon icon = null;
508: if (object instanceof Allocatable) {
509: if (((Allocatable) object).isPerson()) {
510: icon = personIcon;
511: } else {
512: icon = defaultIcon;
513: }
514:
515: } else if (object instanceof DynamicType) {
516: DynamicType type = (DynamicType) object;
517: String classificationType = type
518: .getAnnotation(DynamicTypeAnnotations.KEY_CLASSIFICATION_TYPE);
519: if (DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION
520: .equals(classificationType)) {
521: setBorder(conflictBorder);
522: } else {
523: icon = folderClosedIcon;
524: }
525: }
526: if (icon == null) {
527: setBorder(nonIconBorder);
528: }
529: setLeafIcon(icon);
530: }
531:
532: public Component getTreeCellRendererComponent(JTree tree,
533: Object value, boolean sel, boolean expanded,
534: boolean leaf, int row, boolean hasFocus) {
535: setBorder(null);
536: setFont(normalFont);
537: setClosedIcon(folderClosedIcon);
538: setOpenIcon(folderOpenIcon);
539: if (value != null && value instanceof TypeNode) {
540: TypeNode typeNode = (TypeNode) value;
541: Icon bigFolderIcon;
542: if (typeNode.getType().equals(User.TYPE)) {
543: bigFolderIcon = getI18n().getIcon(
544: "icon.big_folder_users");
545: } else if (typeNode.getType().equals(Period.TYPE)) {
546: bigFolderIcon = getI18n().getIcon(
547: "icon.big_folder_periods");
548: } else if (typeNode.getType().equals(Reservation.TYPE)) {
549: if (typeNode.isFiltered()) {
550: bigFolderIcon = getI18n().getIcon(
551: "icon.big_folder_events_filtered");
552: } else {
553: bigFolderIcon = getI18n().getIcon(
554: "icon.big_folder_events");
555: }
556: } else if (typeNode.getType().equals(Conflict.TYPE)) {
557: bigFolderIcon = getI18n().getIcon(
558: "icon.big_folder_conflicts");
559: } else {
560: if (typeNode.isFiltered()) {
561: bigFolderIcon = getI18n().getIcon(
562: "icon.big_folder_resources_filtered");
563: } else {
564: bigFolderIcon = getI18n().getIcon(
565: "icon.big_folder_resources");
566: }
567: }
568: setClosedIcon(bigFolderIcon);
569: setOpenIcon(bigFolderIcon);
570: setLeafIcon(bigFolderIcon);
571: setFont(bigFont);
572: value = typeNode.getTitle();
573: } else if (value instanceof CategoryNode) {
574: Category category = ((CategoryNode) value)
575: .getCategory();
576: if (category.getParent() == null) {
577: setClosedIcon(getI18n().getIcon(
578: "icon.big_folder_categories"));
579: setOpenIcon(getI18n().getIcon(
580: "icon.big_folder_categories"));
581: setFont(bigFont);
582: } else {
583: boolean hasChildren = category.getCategories().length > 0;
584: if (!hasChildren) {
585: setClosedIcon(null);
586: setOpenIcon(null);
587: setLeafIcon(null);
588: setBorder(nonIconBorder);
589: }
590: }
591: } else {
592: Object nodeInfo = getTheUserObject(value);
593: if (nodeInfo instanceof Conflict) {
594: Conflict conflict = (Conflict) nodeInfo;
595: String text = getInfoFactory().getToolTip(conflict);
596: value = text;
597: setBorder(conflictBorder);
598: setLeafIcon(null);
599: } else {
600: setClosedIcon(folderClosedIcon);
601: setOpenIcon(folderOpenIcon);
602: if (leaf) {
603: setLeaf(nodeInfo);
604: }
605: }
606: }
607: Component result = super .getTreeCellRendererComponent(tree,
608: value, sel, expanded, leaf, row, hasFocus);
609: return result;
610: }
611:
612: }
613:
614: private static Object getTheUserObject(Object node) {
615: if (node instanceof DefaultMutableTreeNode)
616: return ((DefaultMutableTreeNode) node).getUserObject();
617: if (node instanceof RecursiveNode)
618: return ((RecursiveNode) node).getUserObject();
619: return node;
620: }
621:
622: class RaplaTreeToolTipRenderer implements TreeToolTipRenderer {
623: public String getToolTipText(JTree tree, int row) {
624: Object node = tree.getPathForRow(row)
625: .getLastPathComponent();
626: Object value = getTheUserObject(node);
627: if (value instanceof Conflict) {
628: return null;
629: }
630: return getInfoFactory().getToolTip(value);
631: }
632: }
633:
634: }
|