0001: /**
0002: * Copyright 2004 Carlos Silva A.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: *
0016: */package com.csa.lgantt.model;
0017:
0018: import java.io.IOException;
0019: import java.util.Date;
0020: import java.util.Enumeration;
0021: import java.util.Hashtable;
0022: import java.util.Iterator;
0023: import java.util.Stack;
0024: import java.util.Vector;
0025:
0026: import com.csa.lgantt.Messages;
0027: import com.csa.lgantt.cmds.Command;
0028: import com.csa.lgantt.cmds.SetCommand;
0029: import com.csa.lgantt.cmds.TaskCommand;
0030:
0031: /**
0032: * Tarea de una carta gantt, contiene los datos necesarios para cada tarea. Se
0033: * enlaza con sus tareas hijas a traves de {@link #getChilds()} y
0034: * {@link #addChild()}.
0035: *
0036: * Los plugins pueden acceder y modificar el arbol de tareas directamente a
0037: * traves de este objeto y desde las funciones del objeto {@link Project}.
0038: *
0039: *
0040: * TODO: (A) Support real lengths (3h) and virtual lengths (3d) which depend on
0041: * the current amount of day work.
0042: *
0043: * @version $Header:
0044: * /cvs/java/App_JGantt/source/com/csa/lgantt/model/Task.java,v 1.1
0045: * 2006/09/14 08:14:17 csilva Exp $
0046: * @author Carlos Silva
0047: */
0048: public class Task {
0049: // Instancia
0050: /**
0051: * id
0052: */
0053: protected int id = 0;
0054: /**
0055: * Nombre
0056: */
0057: protected String name = Messages.getString("task.defaultName"); //$NON-NLS-1$
0058: /**
0059: * task length in milliseconds
0060: */
0061: protected long msLength = 0;
0062: /**
0063: * Fecha antes de la cual la tarea no debe comenzar. Esta restriccion solo
0064: * aplica a tareas no contenedoras.
0065: */
0066: protected Date notBeforeDate = null;
0067: /**
0068: * Comentarios
0069: */
0070: protected String comments = Messages
0071: .getString("task.defaultComment"); //$NON-NLS-1$
0072: /**
0073: * Porcentaje completado de la tarea
0074: */
0075: protected int completed = 0;
0076: /**
0077: * Prioridad de la tarea (numero entero)
0078: */
0079: protected int priority = 0;
0080: /**
0081: * indicador si ta tarea se marca como hito.
0082: */
0083: protected boolean hito = false;
0084:
0085: /**
0086: * notas
0087: */
0088: protected String notes = "";
0089:
0090: /**
0091: * Largo minimo de la tarea en minutos habiles.
0092: */
0093: int minLength = 0;
0094: /**
0095: * Largo maximo de la tarea en minutos habiles.
0096: */
0097: int maxLength = 0;
0098:
0099: /**
0100: * Datos para ser utilizados por la aplicacion de despliege/edicion. los
0101: * datos son no transientes. sin embargo, al serializar, solo se graban
0102: * objetos de clases String, Number y Date.
0103: */
0104: protected Hashtable attributes = new Hashtable();
0105:
0106: // Asociaciones con otros objetos
0107: /**
0108: * Lista de tareas que componen un resumen. Esta tarea es un resumen si
0109: * childTasks.size()>0
0110: */
0111: protected Vector childTasks = new Vector();
0112: /**
0113: * Tarea padre (o resumen) de esta tarea.
0114: */
0115: protected Task parentTask = null;
0116: /**
0117: * lista de dependencias que nacen de esta tarea Esta variable es llenada por
0118: * la clase {@link Constraint}
0119: */
0120: protected Vector childConstraints = new Vector();
0121: /**
0122: * lista de dependencias que afectan a esta tarea. Esta variable es llenada
0123: * por la clase {@link Constraint}
0124: */
0125: protected Vector constraints = new Vector();
0126: /**
0127: * Referencia al proyecto al cual pertenecen las tareas
0128: */
0129: protected Project project = null;
0130: /**
0131: * Recursos asociados a la ejecucion de la tarea Contiene elementos de tipo
0132: * {@link Asignation}
0133: */
0134: protected Vector resources = new Vector();
0135:
0136: // Transientes
0137: /**
0138: * Fecha de inicio calculada segun el {@link DateOrganizer}. Solo es valida
0139: * si dirty==false
0140: */
0141: transient Date startDate = null;
0142: /**
0143: * Indicacion de si la tarea requiere recalcularse
0144: */
0145: transient boolean dirty = true;
0146: /**
0147: * fecha de termino de la tarea solo es valida si dirty==false
0148: */
0149: transient Date finishDate = null;
0150: /**
0151: * indicacion de si la tarea se esta borrando para no enviar multiples
0152: * notificaciones al borrar un arbol.
0153: *
0154: * @param p
0155: */
0156: transient boolean removed = false;
0157:
0158: /**
0159: * Contructor de una tarea. Inicializa el proyecto como una referencia
0160: * interna.
0161: */
0162: public Task(Project p) {
0163: project = p;
0164: }
0165:
0166: /**
0167: * Crea una tarea a partir de un proyecto y un nombre
0168: *
0169: * @param p
0170: * @param name
0171: */
0172: public Task(Project p, String name) {
0173: project = p;
0174: setName(name);
0175: }
0176:
0177: /**
0178: * Crea una tarea a partir de un proyecto, un nombre y una duracion en dias.
0179: *
0180: * @param p
0181: * @param name
0182: * @param length
0183: */
0184: public Task(Project p, String name, long msLength) {
0185: project = p;
0186: this .name = name;
0187: try {
0188: // do not use setLength() because it will generate an undo command...
0189: this .msLength = msLength;
0190: setDirty();
0191: } catch (Exception e) {
0192: // nunca ocurrira, esta tarea nueva no tiene hijos.
0193: }
0194: }
0195:
0196: /**
0197: * Datos para ser utilizados por la aplicacion de despliege/edicion En la
0198: * lista de atributos se puede almacenar cualquier valor (no null)
0199: */
0200: public Object getAttribute(String aName) {
0201: return attributes.get(aName);
0202: }
0203:
0204: /**
0205: * Datos para ser utilizados por la aplicacion de despliege/edicion. En la
0206: * lista de atributos se puede almacenar cualquier valor.
0207: */
0208: public void setAttribute(String name, Object data) {
0209: attributes.put(name, data);
0210: }
0211:
0212: /**
0213: * Datos para ser utilizados por la aplicacion de despliege/edicion
0214: */
0215: public void removeAttribute(String attrName) {
0216: attributes.remove(attrName);
0217: }
0218:
0219: // Metodos protegidos
0220: /**
0221: * Asigna el Id de la tarea. Este Id es interno, parte de 0 para la tarea
0222: * principal (invisible) del proyecto y es asignada por el proyecto.
0223: */
0224: protected void setId(int i) {
0225: id = i;
0226: }
0227:
0228: /**
0229: * retorna el Id de la tarea. La tarea principal del proyecto (invisible)
0230: * tiene Id ==0.
0231: */
0232: public int getId() {
0233: return id;
0234: }
0235:
0236: /**
0237: * indica si la tarea es un resumen de otras tareas.
0238: */
0239: public boolean isResume() {
0240: return childTasks.size() > 0;
0241: }
0242:
0243: /**
0244: * Remueve la tarea del proyecto. para remover la tarea se remueven sus
0245: * constraints de la lista de objetos. ademas se remueve de la lista de hijos
0246: * de la tarea resumen y llama a la funcion removeTask() del proyecto.
0247: * </P>
0248: * </P>
0249: * Esta funcion puede ser llamada por los elementos externos. notifica del
0250: * borrado llamando a la funcion {@link Project#removeTask}.
0251: * </P>
0252: * <P>
0253: * Esta funcion solo notifica la eliminacion del primer hijo. No envia
0254: * notificaciones para las tareas hijas que dependen.
0255: * </P>
0256: */
0257: public void remove() {
0258: removed = true;
0259: for (Enumeration e = childConstraints.elements(); e
0260: .hasMoreElements();) {
0261: Constraint c = (Constraint) e.nextElement();
0262: c.getSlaveTask().setDirty();
0263: c.remove();
0264: }
0265: for (Enumeration e = constraints.elements(); e
0266: .hasMoreElements();) {
0267: Constraint c = (Constraint) e.nextElement();
0268: c.remove();
0269: }
0270: // borrar los hijos.
0271: for (Enumeration e = childTasks.elements(); e.hasMoreElements();) {
0272: Task t = (Task) e.nextElement();
0273: t.remove();
0274: }
0275: parentTask.childTasks.remove(this );
0276: parentTask.setDirty();
0277: // Solo notifica si esta es la tarea principal borrada.
0278:
0279: if (!parentTask.removed) {
0280:
0281: project.childRemoved(this );
0282:
0283: TaskCommand cmd = new TaskCommand(this ) {
0284: public void execute() {
0285: task.remove();
0286: }
0287:
0288: public void undo() {
0289: parentTask.addChild(task);
0290: }
0291: };
0292: project.registerCommand(cmd);
0293: }
0294: }
0295:
0296: /**
0297: * marca esta tarea como una tarea resumen al agregar una tarea hija. Esta
0298: * funcion asigna el Id de la tarea hija dejandola como el ultimo hijo.
0299: */
0300: public void addChild(Task t) {
0301: notBeforeDate = null;
0302: t.parentTask = this ;
0303: int idx = id;
0304: int childs = childTasks.size();
0305:
0306: childTasks.addElement(t);
0307: TaskCommand cmd = new TaskCommand(t) {
0308: public void execute() {
0309: addChild(task);
0310: }
0311:
0312: public void undo() {
0313: task.remove();
0314: }
0315: };
0316: project.registerCommand(cmd);
0317: project.childInserted(t);
0318: setDirty();
0319: }
0320:
0321: /**
0322: * Agrega como hijas de esta tarea el contenido de otro proyecto.
0323: *
0324: * @param p
0325: * proyecto a cargar
0326: */
0327: public void addChild(Project p) {
0328: notBeforeDate = null;
0329: int idx = id;
0330: int childs = childTasks.size();
0331:
0332: for (int i = 0; i < p.taskList.size(); i++) {
0333: ((Task) p.taskList.get(i)).project = project;
0334: }
0335: for (int i = 0; i < p.mainTask.childTasks.size(); i++) {
0336: ((Task) p.mainTask.childTasks.get(i)).parentTask = this ;
0337: }
0338: childTasks.addAll(p.mainTask.childTasks);
0339:
0340: // TODO: Soporte undo/redo para esta operacion
0341: project.childInserted(this );
0342: setDirty();
0343:
0344: }
0345:
0346: /**
0347: * Agrega una tarea como hija pero antes que otra. Esta funcion asigna el Id
0348: * de la tarea hija dejandola como el ultimo hijo.
0349: */
0350: private void addChildBefore(Task newTask, Task before) {
0351: notBeforeDate = null;
0352: newTask.parentTask = this ;
0353: newTask.setId(before.getId());
0354: int childs = childTasks.size();
0355: if (before != null) {
0356: int i = childTasks.indexOf(before);
0357: if (i >= 0)
0358: childTasks.add(i, newTask);
0359: else
0360: childTasks.add(newTask);
0361: } else
0362: childTasks.add(newTask);
0363:
0364: TaskCommand cmd = new TaskCommand(newTask, before) {
0365: public void execute() {
0366: addChildBefore(task, refTask);
0367: }
0368:
0369: public void undo() {
0370: task.remove();
0371: }
0372: };
0373: project.registerCommand(cmd);
0374: project.childInserted(newTask);
0375: }
0376:
0377: /**
0378: * Agrega una tarea como hermana anterior
0379: *
0380: * @param newTask
0381: * tarea nueva
0382: */
0383: public void addBefore(Task newTask) {
0384: getParentTask().addChildBefore(newTask, this );
0385: }
0386:
0387: /**
0388: * Agrega una tarea como hija pero antes que otra. Esta funcion asigna el Id
0389: * de la tarea hija dejandola como el ultimo hijo.
0390: */
0391: private void addChildAfter(Task newTask, Task after) {
0392: notBeforeDate = null;
0393: newTask.parentTask = this ;
0394: newTask.setId(after.getId());
0395: int childs = childTasks.size();
0396: if (after != null) {
0397: int i = childTasks.indexOf(after);
0398: if (i >= 0)
0399: childTasks.add(i + 1, newTask);
0400: else
0401: childTasks.add(newTask);
0402: } else
0403: childTasks.add(newTask);
0404:
0405: TaskCommand cmd = new TaskCommand(newTask, after) {
0406: public void execute() {
0407: addChildAfter(task, refTask);
0408: }
0409:
0410: public void undo() {
0411: task.remove();
0412: }
0413: };
0414: project.registerCommand(cmd);
0415:
0416: project.childInserted(newTask);
0417: }
0418:
0419: /**
0420: * Agrega una tarea como hermana posterior
0421: *
0422: * @param newTask
0423: * tarea nueva
0424: */
0425: public void addAfter(Task newTask) {
0426: getParentTask().addChildAfter(newTask, this );
0427: }
0428:
0429: /**
0430: * Retorna el nombre de la tarea(titulo)
0431: */
0432: public String getName() {
0433: return name;
0434: }
0435:
0436: /**
0437: * Transforma esta tarea en hija de su hermana anterior No deben haber
0438: * contraints entre hermana y hermana (eliminar)
0439: *
0440: */
0441: public void asChild() {
0442: int i = parentTask.childTasks.indexOf(this );
0443: if (i == 0)
0444: return;
0445: parentTask.childTasks.remove(i);
0446: Task t = (Task) parentTask.childTasks.elementAt(i - 1);
0447: // System.out.println("childing " + getId() + " -> " + t.getId());
0448: for (int j = 0; j < t.childConstraints.size(); j++) {
0449: // System.out.println(j);
0450: Constraint c = (Constraint) t.childConstraints.get(j);
0451: // System.out.println("Constraint "+ c.getMasterTask().getId()+ " -> "+
0452: // c.getSlaveTask().getId());
0453: if (hasChild(c.getSlaveTask()))
0454: c.remove();
0455: }
0456: t.addChild(this );
0457: project.taskMoved(this );
0458: }
0459:
0460: /**
0461: * Retorna verdadero si esta tarea depende de otra.
0462: * <p>
0463: * Una tarea <code>A</code> depende de otra <code>B</code> si:
0464: * </p>
0465: * <ol>
0466: * <li>A==B
0467: * <li>A es descendiente de B
0468: * <li>algun ancestro de B tiene un Constraint hacia algun ancestro de A
0469: * <li>algun descendiente de B tiene un constraint hacia algun descendiente
0470: * de A
0471: * </ol>
0472: * <p>
0473: * Otra forma de verlo es: Una tarea <code>A</code> depende de otra
0474: * <code>B</code> si:
0475: * </p>
0476: * <ol>
0477: * <li>El conjunto de descendientes y restrictos de descendientes y
0478: * restrictos incluye a A.
0479: * </ol>
0480: *
0481: * @param master
0482: * Task que puede ser una tarea que contiene una dependencia.
0483: * @return verdadero segun las condiciones enumeradas.
0484: */
0485: public void checkConstrained(Task master) throws GanttException {
0486: Task t = null;
0487:
0488: // 1.- A==B
0489: if (master == this )
0490: throw new GanttException(Messages
0491: .getString("task.error.selfConstraint")); //$NON-NLS-1$
0492: // 2.- A es descendiente de B
0493: if (isChildOf(master))
0494: throw new GanttException(Messages
0495: .getString("task.error.parentConstraint")); //$NON-NLS-1$
0496:
0497: // 3.- Conjunto de restrictos.
0498: Vector set = new Vector();
0499: Stack porRevisar = new Stack();
0500:
0501: porRevisar.push(master);
0502: t = master;
0503: while (t != null) {
0504: set.add(t);
0505: // add constrained tasks
0506: for (Iterator it = t.childConstraints.iterator(); it
0507: .hasNext();) {
0508: Constraint c = (Constraint) it.next();
0509: porRevisar.push(c.getSlaveTask());
0510: }
0511: t = t.parentTask;
0512: }
0513: while (!porRevisar.empty()) {
0514: t = (Task) porRevisar.pop();
0515: set.add(t);
0516: // agregar hijos
0517: for (Iterator it = t.childTasks.iterator(); it.hasNext();)
0518: porRevisar.push(it.next());
0519: // agregar tasks constrained
0520: for (Iterator it = t.childConstraints.iterator(); it
0521: .hasNext();) {
0522: Constraint c = (Constraint) it.next();
0523: porRevisar.push(c.getSlaveTask());
0524: }
0525: }
0526: if (set.contains(this ))
0527: throw new GanttException(Messages
0528: .getString("task.error.dependantConstraint")
0529: + " Task '"
0530: + this .name
0531: + "' depends on '"
0532: + master.name + "'.");
0533: }
0534:
0535: /**
0536: * Convierte una tarea en hermana de su padre.
0537: */
0538: public void deChild() {
0539: if (getChildLevel() == 1)
0540: return;
0541: Task parent = parentTask;
0542: parentTask.childTasks.remove(this );
0543: parentTask.parentTask.addChild(this );
0544: parent.setDirty();
0545: setDirty();
0546: project.taskMoved(this );
0547: }
0548:
0549: /**
0550: * Convierte mueve una herma a su tarea anterior
0551: */
0552: public void goUp() {
0553: int i = parentTask.childTasks.indexOf(this );
0554: if (i == 0)
0555: return;
0556: parentTask.childTasks.add(i - 1, this );
0557: parentTask.childTasks.remove(i + 1);
0558: setDirty();
0559: project.taskMoved(this );
0560: }
0561:
0562: /**
0563: * Convierte mueve una herma a su tarea anterior
0564: */
0565: public void goDown() {
0566: int i = parentTask.childTasks.indexOf(this );
0567: if (i + 1 >= parentTask.childTasks.size())
0568: return;
0569: parentTask.childTasks.add(i + 2, this );
0570: parentTask.childTasks.remove(i);
0571: parentTask.setDirty();
0572: project.taskMoved(this );
0573: }
0574:
0575: /**
0576: * Determina si una tarea es hija de esta.
0577: *
0578: * @param child
0579: * @return true si this tiene como descendiente child
0580: */
0581: public boolean hasChild(Task child) {
0582: if (this == child)
0583: return true;
0584: for (Iterator it = childTasks.iterator(); it.hasNext();) {
0585: Task c = (Task) it.next();
0586: if (c.hasChild(child))
0587: return true;
0588: }
0589: return false;
0590: }
0591:
0592: /**
0593: * getChildLevel retorna el nivel de profundidad de la tarea comienza desde
0594: * 1.
0595: *
0596: * @return profundidad desde 1.
0597: */
0598: public int getChildLevel() {
0599: if (parentTask == null)
0600: return 0;
0601: return parentTask.getChildLevel() + 1;
0602: }
0603:
0604: /**
0605: * Retorna el nombre de la tarea(titulo) con indentacion dependiendo de la
0606: * profundidad en descendientes de la tarea principal del proyecto.
0607: */
0608: public String getIndentedName() {
0609: StringBuffer spaces = new StringBuffer();
0610: Task p = parentTask;
0611: while (p != null) {
0612: spaces.append(" ");
0613: p = p.parentTask;
0614: }
0615: return spaces.toString() + name;
0616: }
0617:
0618: /**
0619: * Agrega una restriccion de la cual esta tarea depende Si ya existe un
0620: * constraint entre estas tareas se genera una excepcion.
0621: *
0622: * @param c
0623: */
0624: void addConstraint(Constraint c) throws GanttException {
0625: c.getMasterTask().checkConstrained(c.getSlaveTask());
0626:
0627: for (Iterator it = c.getMasterTask().childConstraints
0628: .iterator(); it.hasNext();) {
0629: Constraint ct = (Constraint) it.next();
0630: if (ct.getSlaveTask() == this )
0631: throw new GanttException(Messages
0632: .getString("task.error.dupConstraint")); //$NON-NLS-1$
0633: }
0634:
0635: c.getMasterTask().childConstraints.addElement(c);
0636: constraints.addElement(c);
0637: setDirty();
0638:
0639: project.sendChange(new ProjectChange(this ));
0640: }
0641:
0642: /**
0643: * @return the length of the task in ms. This is the total length not
0644: * considering resources.
0645: */
0646: public long getWorkLength() {
0647: return msLength;
0648: }
0649:
0650: /**
0651: * @return a string using numbers and time units as: 1d 4h 5m
0652: */
0653: public String getHumanReadableWorkLength() {
0654: return project.getDateOrganizer().getHumanReadableLap(msLength);
0655: }
0656:
0657: /**
0658: * sets the length using abreviations such as: 5w 3d 2h 5m 58s 235S (five
0659: * weeks, 3 days, 2 hours, 5 minutes, fifty eight seconds and two hundred and
0660: * thirty five milliseconds).
0661: */
0662: public void setHumanReadableWorkLength(String s)
0663: throws GanttException {
0664: setWorkLength(project.getDateOrganizer()
0665: .getLapFromHumanReadable(s));
0666: }
0667:
0668: /**
0669: * Assigns the length of the task in milliseconds
0670: */
0671: public void setWorkLength(long newLength) throws GanttException {
0672: if (isResume()) {
0673: throw new GanttException(Messages
0674: .getString("task.error.chgLength")); //$NON-NLS-1$
0675: }
0676: Command cmd = new SetCommand(this , "setWorkLength", newLength,
0677: msLength);
0678: msLength = newLength;
0679: setDirty();
0680: project.registerCommand(cmd);
0681: project.sendChange(new ProjectChange(this ));
0682: }
0683:
0684: /**
0685: * Asigna el nombre.
0686: */
0687: public void setName(String newName) {
0688: Command cmd = new SetCommand(this , "setName", newName, name);
0689: if (newName == null)
0690: name = "";
0691: else
0692: name = newName;
0693: project.registerCommand(cmd);
0694: project.sendChange(new ProjectChange(this ));
0695: }
0696:
0697: /**
0698: * Retorna la fecha minima en que la tarea puede comenzar
0699: */
0700: public Date getNotBeforeDate() {
0701: return notBeforeDate;
0702: }
0703:
0704: /**
0705: * Asigna la fecha minima en que la tarea puede comenzar
0706: */
0707: public void setNotBeforeDate(Date newNotBeforeDate)
0708: throws GanttException {
0709: if (childTasks.size() > 0) {
0710: throw new GanttException(Messages
0711: .getString("task.error.chgNotBeforeDate")); //$NON-NLS-1$
0712: }
0713:
0714: Command cmd = new SetCommand(this , "setNotBeforeDate",
0715: newNotBeforeDate, notBeforeDate);
0716: notBeforeDate = newNotBeforeDate;
0717: setDirty();
0718: project.registerCommand(cmd);
0719: project.sendChange(new ProjectChange(this ));
0720: }
0721:
0722: /**
0723: * retorna los comentarios asociados a la tarea
0724: */
0725: public String getComments() {
0726: return comments;
0727: }
0728:
0729: /**
0730: * Asigna los comentarios de la tarea
0731: */
0732: public void setComments(String newComments) {
0733: Command cmd = new SetCommand(this , "setComments", newComments,
0734: comments);
0735: comments = newComments;
0736: project.registerCommand(cmd);
0737: project.sendChange(new ProjectChange(this ));
0738: }
0739:
0740: /**
0741: * Retorna la fecha calculada de inicio de la tarea de acuerdo al calendario
0742: * del proyecto
0743: */
0744: public Date getStartDate() {
0745: recalc();
0746: return startDate;
0747: }
0748:
0749: /**
0750: * Retorna la fecha de termino de la tarea en dias reales Asume que una tarea
0751: * de un dia se terminan a ultima hora del mismo dia Todas las tareas
0752: * comienzan a primera hora del dia. Solo cuentan los campos de dia (no los
0753: * de hh,mm,ss y ms)
0754: */
0755: public Date getFinishDate() {
0756: recalc();
0757: return finishDate;
0758: }
0759:
0760: public Vector getChildTasks() {
0761: return childTasks;
0762: }
0763:
0764: // Metodos protegidos
0765:
0766: /**
0767: * Marca la tarea para ser recalculada... Originalmente recalculaba
0768: * recursivamente:
0769: * <ul>
0770: * <li>los hijos: si depende de otras tareas (incluso indirectamente)
0771: * <li>los dependientes
0772: * <li>La tarea padre
0773: * </ul>
0774: *
0775: * Hoy llama a {@link Project#allDirty()} y asigna todas las tareas como
0776: * sucias, esto elimino muchos errores en el recalculo y no toma tanto tiempo
0777: * como para ser un problema. Recursiva sobre las tareas resumen
0778: */
0779: protected void setDirty() {
0780: project.allDirty();
0781: }
0782:
0783: /**
0784: * Obtiene la fecha de inicio basado solo en contraints y en las
0785: * restricciones heredadas de la tarea padre.
0786: * <p>
0787: * Si no hay constraints, se parte desde la fecha de inicio del proyecto.
0788: * </p>
0789: * <p>
0790: * no afecta el estado de dirty.
0791: * </p>
0792: *
0793: * @returns la fecha de inicio de la tarea
0794: */
0795: protected Date getConstrainedStart() {
0796: Date cStart = notBeforeDate;
0797: if (cStart == null)
0798: cStart = project.getStartDate();
0799: for (Enumeration e = constraints.elements(); e
0800: .hasMoreElements();) {
0801: Constraint c = (Constraint) e.nextElement();
0802: Date test = c.getStartDate();
0803: if ((cStart == null) || (test.after(cStart))) {
0804: cStart = test;
0805: }
0806: }
0807:
0808: // Si el resumen padre tiene restricciones, se heredan
0809: if (parentTask != null) {
0810: Date cDate = parentTask.getConstrainedStart();
0811: if (cStart.before(cDate)) {
0812: cStart = cDate;
0813: }
0814: } else { // es la tarea principal. ya tiene un startDate
0815: }
0816: return project.getDateOrganizer().getNextPossibleStart(cStart);
0817: }
0818:
0819: /**
0820: * Calculo de restricciones revisar restricciones. deja el estado limpio (not
0821: * dirty)
0822: */
0823: protected void recalc() {
0824: if (!dirty)
0825: return;
0826:
0827: startDate = getConstrainedStart();
0828: if (childTasks.size() > 0) { // es tarea resumen? tiene tareas hijas
0829: // inicio=min({child startdates})
0830: Date minChild = null;
0831: Date maxChild = null;
0832: for (Enumeration e = childTasks.elements(); e
0833: .hasMoreElements();) {
0834: Task t = (Task) e.nextElement();
0835: Date cDate = t.getStartDate();
0836: if ((minChild == null) || (minChild.after(cDate))) {
0837: minChild = cDate;
0838: }
0839: cDate = t.getFinishDate();
0840: if ((maxChild == null) || (maxChild.before(cDate))) {
0841: maxChild = cDate;
0842: }
0843: }
0844: if (minChild != null)
0845: startDate = minChild;
0846: finishDate = maxChild;
0847:
0848: DateOrganizer dOrg = project.getDateOrganizer();
0849: msLength = dOrg.getElapsedWork(startDate, finishDate);
0850:
0851: double a = 0, b = 0;
0852: for (Enumeration e = childTasks.elements(); e
0853: .hasMoreElements();) {
0854: Task t = (Task) e.nextElement();
0855: a += (t.getWorkLength() * t.getCompleted()) / 100.0;
0856: b += t.getWorkLength();
0857: }
0858: completed = (int) (100.0 * (a / b));
0859: } else { // es una hoja!
0860: finishDate = project.getDateOrganizer().getEndDate(
0861: startDate, msLength);
0862: }
0863: dirty = false;
0864: }
0865:
0866: public Vector getChildConstraints() {
0867: return childConstraints;
0868: }
0869:
0870: /**
0871: * Retorna los minutos totales asociados a un recurso.
0872: *
0873: * @param r
0874: * recurso
0875: * @return
0876: */
0877: public int getTotalResourceUnits(Resource r) {
0878: int childUnits = 0;
0879: if (childTasks.size() != 0) { // es tarea resumen?
0880: for (Enumeration e = childTasks.elements(); e
0881: .hasMoreElements();) {
0882: Task t = (Task) e.nextElement();
0883: childUnits += t.getTotalResourceUnits(r);
0884: }
0885: }
0886: return getLocalTotalResourceUnits(r) + childUnits;
0887: }
0888:
0889: /**
0890: * Retorna los minutos totales asociados a un recurso solo en esta tarea.
0891: *
0892: * @param r
0893: * recurso
0894: * @return work in MINUTES associated to a resource.
0895: */
0896: public int getLocalTotalResourceUnits(Resource r) {
0897: int units = 0;
0898: int minutes = (int) (msLength / 60000);
0899: for (Iterator i = getAsignations().iterator(); i.hasNext();) {
0900: Asignation a = (Asignation) i.next();
0901: if (a.getResource().equals(r)) {
0902: units = (int) (a.getUnits() * minutes / 100.0);
0903: break;
0904: }
0905: }
0906: return units;
0907: }
0908:
0909: /**
0910: * Work in this subtree for a specific resource. Includes work in child
0911: * tasks.
0912: *
0913: * @return work (in minutes)
0914: */
0915: public int getWorkedResourceUnits(Resource r) {
0916: int childUnits = 0;
0917: if (childTasks.size() != 0) { // es tarea resumen?
0918: for (Enumeration e = childTasks.elements(); e
0919: .hasMoreElements();) {
0920: Task t = (Task) e.nextElement();
0921: childUnits += t.getWorkedResourceUnits(r);
0922: }
0923: }
0924: return getLocalWorkedResourceUnits(r) + childUnits;
0925: }
0926:
0927: /**
0928: * Work in this task for a a resource. Does not include child work.
0929: *
0930: * @return work (in minutes)
0931: */
0932: public int getLocalWorkedResourceUnits(Resource r) {
0933: int units = 0;
0934: int minutes = (int) (msLength / 60000);
0935: for (Iterator i = getAsignations().iterator(); i.hasNext();) {
0936: Asignation a = (Asignation) i.next();
0937: if (a.getResource().equals(r)) {
0938: units = (int) (a.getUnits() * minutes / 100.0);
0939: units = (int) (units * getCompleted() / 100.0);
0940: break;
0941: }
0942: }
0943: return units;
0944: }
0945:
0946: /**
0947: * Asigna el nivel de completitud de la tarea.
0948: *
0949: * @param c
0950: * completitud en un porcentaje en el rango 0..100
0951: */
0952: public void setCompleted(int newCompleted) {
0953: if (childTasks.size() != 0) { // es tarea resumen?
0954: throw new RuntimeException(Messages
0955: .getString("task.error.chgCompleted")); //$NON-NLS-1$
0956: }
0957: Command cmd = new SetCommand(this , "setCompleted",
0958: newCompleted, completed);
0959: completed = newCompleted;
0960: setDirty();
0961: project.registerCommand(cmd);
0962: project.sendChange(new ProjectChange(this ));
0963: }
0964:
0965: /**
0966: * Obtiene el porcentaje terminado de una tarea. En el caso de las tareas
0967: * resumen, el total se calcula en la funcion recalc
0968: *
0969: * @return
0970: */
0971: public int getCompleted() {
0972: return completed;
0973: }
0974:
0975: /**
0976: * Reasigna al recurso con una cantidad de unidades Si ya esta asignado, lo
0977: * reasigna. Si las unidades son cero, se elimina la asignacion existente (o
0978: * no se agrega).
0979: *
0980: * @param r
0981: * @param u
0982: */
0983: public void addResource(Resource r, int u) {
0984: removeResource(r);
0985: if (u == 0) {
0986: project.sendChange(new ProjectChange(this ));
0987: return;
0988: }
0989: Asignation a = new Asignation();
0990: a.setResource(r);
0991: a.setUnits(u);
0992: resources.addElement(a);
0993: project.sendChange(new ProjectChange(this ));
0994: }
0995:
0996: /**
0997: * Remueve un recurso de la lista de recursos
0998: *
0999: */
1000: public void removeResource(Resource r) {
1001: for (int i = 0; i < resources.size(); i++) {
1002: Asignation a = (Asignation) resources.elementAt(i);
1003: if (a.getResource().equals(r)) {
1004: resources.remove(i);
1005: return;
1006: }
1007: }
1008: project.sendChange(new ProjectChange(this ));
1009: }
1010:
1011: public Vector getAsignations() {
1012: return resources;
1013: }
1014:
1015: /**
1016: *
1017: * @param r
1018: * @return
1019: */
1020: public int getAsignationUnits(Resource r) {
1021: for (Iterator i = resources.iterator(); i.hasNext();) {
1022: Asignation a = (Asignation) i.next();
1023: if (a.getResource() == r)
1024: return a.getUnits();
1025: }
1026: return 0;
1027: }
1028:
1029: public Task getParentTask() {
1030: return parentTask;
1031: }
1032:
1033: public boolean isChildOf(Task p) {
1034: Task t = this ;
1035: while ((t.parentTask != null) && (t.parentTask != p)) {
1036: t = t.parentTask;
1037: }
1038: return (p == t.parentTask);
1039: }
1040:
1041: // serializacion
1042: private void readObject(java.io.ObjectInputStream in)
1043: throws IOException, ClassNotFoundException {
1044: in.defaultReadObject();
1045: // reiniciar variables transientes...
1046: dirty = true;
1047:
1048: }
1049:
1050: /**
1051: * Retorna un string con las asignaciones de personas
1052: *
1053: * @return
1054: */
1055: public String getCompletionDescription() {
1056: Vector a = getAsignations();
1057: StringBuffer toPrint = new StringBuffer(30);
1058: for (int j = 0; j < a.size(); j++) {
1059: Asignation asign = (Asignation) a.elementAt(j);
1060: toPrint.append(" ");
1061: toPrint.append(asign.getResource().getInitials());
1062: toPrint.append("[");
1063: toPrint.append(asign.getUnits());
1064: toPrint.append("%]");
1065: }
1066: // toPrint.append(" - ");
1067: // toPrint.append(getCompleted());
1068: // toPrint.append("%");
1069: return toPrint.toString();
1070: }
1071:
1072: /**
1073: * @see java.lang.Object#toString()
1074: */
1075: public String toString() {
1076: return "Task(" + name + ")";
1077: }
1078:
1079: int visibleIndex = 0;
1080:
1081: /**
1082: * indice visible
1083: *
1084: * @return indice 1..
1085: */
1086: public int getVisibleIndex() {
1087: return visibleIndex;
1088: }
1089:
1090: boolean childsVisible = true;
1091:
1092: public boolean getChildsVisible() {
1093: if (!isResume())
1094: return true;
1095: return childsVisible;
1096:
1097: }
1098:
1099: public void setChildsVisible(boolean b) {
1100: childsVisible = b;
1101: // reorganizar los ids visibles
1102: project.rebuildVIds();
1103:
1104: project.registerCommand(new SetCommand(this ,
1105: "setChildsVisible", b, !b));
1106: project.sendChange(new ProjectChange(this ));
1107: }
1108:
1109: public boolean isVisible() {
1110: Task p = parentTask;
1111: while (p != null) {
1112: if (!p.childsVisible)
1113: return false;
1114: p = p.parentTask;
1115: }
1116: return true;
1117: }
1118:
1119: /**
1120: * Retorna una referencia al objeto que administra los colores de esta tarea
1121: *
1122: */
1123: public TaskColors getTaskColors() {
1124: if (isResume())
1125: return project.getGraphColors().defaultResumeColors;
1126: return project.getGraphColors().defaultTaskColors;
1127: }
1128:
1129: Vector snapshots = new Vector();
1130:
1131: /**
1132: * Almacena el estado de la tarea en un snapshot
1133: *
1134: * @param date
1135: * @param name
1136: */
1137: public void takeSnapshot(Date date) {
1138: snapshots.add(new SnapShot(date, this ));
1139: }
1140:
1141: /**
1142: * Quita un snapshot de la lista
1143: *
1144: * @param date
1145: */
1146: public void removeSnapshot(Date date) {
1147: for (int i = 0; i < snapshots.size(); i++) {
1148: SnapShot ss = (SnapShot) snapshots.get(i);
1149: if (ss.date.equals(date)) {
1150: snapshots.remove(ss);
1151: }
1152: }
1153: }
1154:
1155: public SnapShot getSnapshot(Date d) {
1156: for (int i = 0; i < snapshots.size(); i++) {
1157: SnapShot ss = (SnapShot) snapshots.get(i);
1158: if (ss.date.equals(d))
1159: return ss;
1160: }
1161: return null;
1162: }
1163:
1164: public static class SnapShot {
1165: public Date date;
1166: public Date start, end;
1167: int completed;
1168:
1169: public SnapShot(Date d, Task t) {
1170: date = d;
1171: start = t.getStartDate();
1172: end = t.getFinishDate();
1173: completed = t.getCompleted();
1174: }
1175:
1176: /**
1177: * Constructor para cuando se lee desde un XML.
1178: *
1179: * @param d
1180: * @param t
1181: */
1182: public SnapShot(Date d, Date s, Date e, int c) {
1183: date = d;
1184: start = s;
1185: end = e;
1186: completed = c;
1187: }
1188: }
1189:
1190: public int getPriority() {
1191: return priority;
1192: }
1193:
1194: public void setPriority(int p) {
1195: Command cmd = new SetCommand(this , "setPriority", p, priority);
1196: priority = p;
1197: project.registerCommand(cmd);
1198: project.sendChange(new ProjectChange(this ));
1199: }
1200:
1201: public int getMinLength() {
1202: return minLength;
1203: }
1204:
1205: public int getMaxLength() {
1206: return maxLength;
1207: }
1208:
1209: public void setMinLength(int newLength) {
1210: Command cmd = new SetCommand(this , "setMinLength", newLength,
1211: minLength);
1212: minLength = newLength;
1213: project.registerCommand(cmd);
1214: project.sendChange(new ProjectChange(this ));
1215:
1216: }
1217:
1218: public void setMaxLength(int l) {
1219: Command cmd = new SetCommand(this , "setMaxLength", l, maxLength);
1220: maxLength = l;
1221: project.registerCommand(cmd);
1222: project.sendChange(new ProjectChange(this ));
1223: }
1224:
1225: public String getNotes() {
1226: return notes;
1227: }
1228:
1229: public void setNotes(String newNotes) {
1230: Command cmd = new SetCommand(this , "setNotes", newNotes, notes);
1231: notes = newNotes;
1232: project.registerCommand(cmd);
1233: project.sendChange(new ProjectChange(this));
1234: }
1235:
1236: }
|