001: package com.technoetic.xplanner.domain;
002:
003: import java.beans.PropertyDescriptor;
004: import java.util.Collection;
005: import java.util.HashMap;
006:
007: import net.sf.hibernate.HibernateException;
008: import org.apache.commons.beanutils.PropertyUtils;
009: import org.apache.log4j.Logger;
010:
011: import com.technoetic.xplanner.db.hibernate.ThreadSession;
012: import com.technoetic.xplanner.views.IterationPage;
013: import com.technoetic.xplanner.views.IterationStoriesPage;
014:
015: //DEBT Should merge with RelationshipMappingRegistry
016: public class DomainMetaDataRepository {
017: private static Logger log = Logger
018: .getLogger(DomainMetaDataRepository.class);
019:
020: private HashMap metadataByClass = new HashMap();
021: private HashMap metadataByTypeName = new HashMap();
022:
023: //DEBT Start using these constant everywhere especially in the web tier (all links)
024: public static final String PROJECT_TYPE_NAME = "project";
025: public static final String ITERATION_TYPE_NAME = "iteration";
026: public static final String STORY_TYPE_NAME = "userstory";
027: public static final String TASK_TYPE_NAME = "task";
028: public static final String FEATURE_TYPE_NAME = "feature";
029: public static final String TIME_ENTRY_TYPE_NAME = "timeentry";
030: public static final String INTEGRATION_TYPE_NAME = "integration";
031: public static final String PERSON_TYPE_NAME = "person";
032: public static final String NOTE_TYPE_NAME = "note";
033:
034: private static DomainMetaDataRepository instance = createInstance();
035:
036: public DomainMetaDataRepository() {
037: }
038:
039: private static DomainMetaDataRepository createInstance() {
040: DomainMetaDataRepository rep = new DomainMetaDataRepository();
041: rep.init();
042: return rep;
043: }
044:
045: public void init() {
046: DomainClass domainClass;
047:
048: //DEBT(DATADRIVEN) Why do we need to specify the /do/ in all entities except iteration?
049: //DEBT(DATADRIVEN) Should have an acceptance tests to check all the links values accross all entities
050: //DEBT(DATADRIVEN) Remove duplication with edit/delete actions
051: //DEBT(DATADRIVEN) Remove duplication within the same action mapping
052:
053: domainClass = new DomainClass(PROJECT_TYPE_NAME, Project.class);
054: domainClass.addMapping(new ActionMapping("edit",
055: "action.edit.project", "admin.edit",
056: "/images/edit.gif", "/do/edit/project", "project",
057: false));
058: domainClass.addMapping(new ActionMapping("delete",
059: "action.delete.project", "sysadmin.delete",
060: "/images/delete.gif", "/do/delete/project", "project",
061: true, true, "action.delete.confirmation"));
062: add(domainClass);
063:
064: domainClass = new DomainClass(ITERATION_TYPE_NAME,
065: Iteration.class, "projectId", Project.class,
066: "iterations");
067: domainClass.addMapping(new ActionMapping("edit",
068: "action.edit.iteration", "edit", "/images/edit.gif",
069: "edit/iteration", "iteration", false));
070: domainClass.addMapping(new ActionMapping("delete",
071: "action.delete.iteration", "delete",
072: "/images/delete.gif", "delete/iteration", "iteration",
073: true, true, "action.delete.confirmation"));
074: domainClass.addMapping(new ActionMapping("createStory",
075: "iteration.link.create_story", "edit", null,
076: "edit/userstory", "iteration", false, false, null));
077: domainClass.addMapping(new ActionMapping("start",
078: IterationPage.START_ACTION, "edit", null,
079: "start/iteration", "iteration", false) {
080: public boolean isVisible(DomainObject object) {
081: return !((Iteration) object).isActive();
082: }
083: });
084: domainClass.addMapping(new ActionMapping("close",
085: IterationPage.CLOSE_ACTION, "edit", null,
086: "close/iteration", "iteration", false) {
087: public boolean isVisible(DomainObject object) {
088: return ((Iteration) object).isActive();
089: }
090: });
091: domainClass.addMapping(new ActionMapping("importStories",
092: IterationStoriesPage.IMPORT_STORIES_LINK, "edit", null,
093: "import/stories", "iteration", false));
094:
095: add(domainClass);
096:
097: domainClass = new DomainClass(STORY_TYPE_NAME, UserStory.class,
098: "iterationId", Iteration.class, "userStories");
099: domainClass.addMapping(new ActionMapping("edit",
100: "action.edit.story", "edit", "/images/edit.gif",
101: "/do/edit/userstory", "story", false));
102: domainClass.addMapping(new ActionMapping("delete",
103: "action.delete.story", "delete", "/images/delete.gif",
104: "/do/delete/userstory", "story", true, true,
105: "action.delete.confirmation"));
106: domainClass.addMapping(new ActionMapping("movecontinue",
107: "action.movecontinue.story", "edit",
108: "/images/movecontinue.gif",
109: "/do/move/continue/userstory", "story", true));
110: add(domainClass);
111:
112: domainClass = new DomainClass(TASK_TYPE_NAME, Task.class,
113: "story", UserStory.class, "tasks");
114: domainClass.addMapping(new ActionMapping("edit",
115: "action.edit.task", "edit", "/images/edit.gif",
116: "/do/edit/task", "task", false));
117: domainClass.addMapping(new ActionMapping("delete",
118: "action.delete.task", "delete", "/images/delete.gif",
119: "/do/delete/task", "task", true, true,
120: "action.delete.confirmation"));
121: domainClass.addMapping(new ActionMapping("movecontinue",
122: "action.movecontinue.task", "edit",
123: "/images/movecontinue.gif", "/do/move/continue/task",
124: "task", true));
125: domainClass.addMapping(new ActionMapping("edittime",
126: "action.edittime.task", "edit", "/images/clock2.gif",
127: "/do/edit/time", "task", false));
128: add(domainClass);
129:
130: add(new DomainClass(TIME_ENTRY_TYPE_NAME, TimeEntry.class,
131: "taskId", Task.class, "timeEntries"));
132:
133: add(new DomainClass(INTEGRATION_TYPE_NAME, Integration.class));
134:
135: domainClass = new DomainClass(PERSON_TYPE_NAME, Person.class);
136: domainClass.addMapping(new ActionMapping("edit",
137: "action.edit.person", "edit", "/images/edit.gif",
138: "/do/edit/person", "person", false));
139: add(domainClass);
140:
141: add(new DomainClass(NOTE_TYPE_NAME, Note.class, "attachedToId",
142: DomainObject.class, null));
143: }
144:
145: public void add(DomainClass domainClass) {
146: metadataByClass.put(domainClass.getJavaClass(), domainClass);
147: metadataByTypeName.put(domainClass.getTypeName(), domainClass);
148: }
149:
150: //DEBT(SPRING) Finish injecting fully this singleton
151: public static DomainMetaDataRepository getInstance() {
152: return instance;
153: }
154:
155: public String classToTypeName(Class objectClass) {
156: return getMetaData(objectClass).getTypeName();
157: }
158:
159: public DomainObject getParent(DomainObject child)
160: throws HibernateException {
161: DomainClass metaData = getMetaData(child.getClass());
162: int parentId = getId(child, metaData.getParentProperty());
163: return (DomainObject) ThreadSession.get().get(
164: metaData.getParentClass(), new Integer(parentId));
165: }
166:
167: public void setParent(DomainObject child, DomainObject parent) {
168: DomainClass metaData = getMetaData(child.getClass());
169: setObjectOrId(child, metaData.getParentProperty(), parent);
170: Collection children = (Collection) getProperty(parent, metaData
171: .getChildrenProperty());
172: children.add(child);
173: }
174:
175: public DomainObject getObject(String type, int id)
176: throws HibernateException {
177: Class targetClass = getMetaData(type).getJavaClass();
178: return (DomainObject) ThreadSession.get().get(targetClass,
179: new Integer(id));
180: }
181:
182: public int getParentId(DomainObject object) {
183: return getId(object, getMetaData(object.getClass())
184: .getParentProperty());
185: }
186:
187: public HashMap getMetadataByTypeName() {
188: return metadataByTypeName;
189: }
190:
191: public DomainClass getMetaData(Class objectClass) {
192: return (DomainClass) metadataByClass.get(objectClass);
193: }
194:
195: public DomainClass getMetaData(String type) {
196: return (DomainClass) metadataByTypeName.get(type);
197: }
198:
199: private static int getId(DomainObject object, String property) {
200: if (property == null)
201: return 0;
202: Object value = getProperty(object, property);
203: if (value == null)
204: return 0;
205: if (value instanceof Identifiable) {
206: return ((Identifiable) value).getId();
207: } else {
208: return ((Integer) value).intValue();
209: }
210: }
211:
212: private static Object getProperty(DomainObject object,
213: String property) {
214: Object prop = null;
215: try {
216: prop = PropertyUtils.getProperty(object, property);
217: } catch (Exception e) {
218: log.error("configuration error with "
219: + (object != null ? object.getClass().toString()
220: : "null") + "." + property, e);
221: }
222: return prop;
223: }
224:
225: private static void setObjectOrId(DomainObject object,
226: String property, DomainObject value) {
227: if (property != null) {
228: try {
229: PropertyDescriptor descriptor = PropertyUtils
230: .getPropertyDescriptor(object, property);
231: Class type = descriptor.getPropertyType();
232: Object target;
233: if (DomainObject.class.isAssignableFrom(type)) {
234: target = value;
235: } else {
236: target = new Integer(value.getId());
237: }
238: PropertyUtils.setProperty(object, property, target);
239: } catch (Exception e) {
240: log.error("configuration error with "
241: + (object != null ? object.getClass()
242: .toString() : "null") + "." + property,
243: e);
244: }
245: }
246: }
247: }
|