Initial commit

This commit is contained in:
Alberto Garcia 2009-04-14 17:51:03 +02:00 committed by Javier Moran Rua
commit 4f4fe94a8c
93 changed files with 4854 additions and 0 deletions

54
README.txt Normal file
View file

@ -0,0 +1,54 @@
* DB creation
-----------
+ Start MySQL:
- Unix: mysqld --default-table-type=InnoDB
- Windows: mysqld-nt --default-table-type=InnoDB(Windows).
+ Create DB "navaldev" (for development):
- mysqladmin -u root create navaldev
+ Create user "naval" with password "naval":
- mysql -u root
GRANT ALL PRIVILEGES ON navaldev.* to naval@localhost IDENTIFIED BY 'naval';
+ Create another DB with name "navaldevtest" (for testing). The user created
above will need to access this new DB.
- mysqladmin -u root create navaldevtest
- mysql -u root
GRANT ALL PRIVILEGES ON navaldevtest.* to naval@localhost IDENTIFIED BY 'naval';
+ PostgreSQL -> DB name=navaldev, user=naval, password=naval.
* Compilation
-----------
+ Download Spring Framework 2.5.6.
+ mvn install:install-file -DgroupId=javax.transaction -DartifactId=jta \
-Dversion=1.0.1B -Dpackaging=jar \
-Dfile=<<spring-framework-2.5.6>>/lib/j2ee/jta.jar
+ cd navalplanner
+ mvn install
+ cd navalplanner-webapp
+ mvn jetty:run
+ Access to http://localhost:8080/navalplanner-webapp.
+ To install the web application in a web container, use the WAR file:
navalplanner-webapp/target/navalplanner-webapp.war
+ NOTE: For PostgreSQL: mvn -Pdev,postgresql install
* Profiles
--------
Check <profiles> section in the root pom.xml to see the profile-based approach
used in the project. The default profiles (the one assumed by the above
instructions) are "dev" and "mysql" (meaning "use MySQL assuming a development
environment").

View file

@ -0,0 +1,38 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.navalplanner</groupId>
<artifactId>navalplanner</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>navalplanner-business</artifactId>
<packaging>jar</packaging>
<name>Naval Planner Business Module</name>
<dependencies>
<dependency>
<groupId>${jdbcDriver.groupId}</groupId>
<artifactId>${jdbcDriver.artifactId}</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,10 @@
package org.navalplanner.business;
public class BusinessGlobalNames {
public final static String BUSINESS_SPRING_CONFIG_FILE =
"classpath:/navalplanner-business-spring-config.xml";
private BusinessGlobalNames () {}
}

View file

@ -0,0 +1,20 @@
package org.navalplanner.business.common.daos;
import java.io.Serializable;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
public interface IGenericDao <E, PK extends Serializable>{
/**
* It updates and inserts the object passed as a parameter.
*/
public void save(E entity);
public E find(PK id) throws InstanceNotFoundException;
public boolean exists(PK id);
public void remove(PK id) throws InstanceNotFoundException;
}

View file

@ -0,0 +1,109 @@
package org.navalplanner.business.common.daos.impl;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.navalplanner.business.common.daos.IGenericDao;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
/**
* All Hibernate DAOs must extend directly from this class. This constraint is
* imposed by the constructor of this class that must infer the type of the
* entity from the concrete DAO declaration.
*
* The class autowires a SessionFactory bean and allows to implement DAOs with
* the Hibernate's native API. Subclasses access Hibernate's Session by calling
* on getSession() method. Operations must be implemented by catching
* HibernateException and rethrowing it by using
* convertHibernateAccessException() method. See source code of this class
* for an example.
*/
public class GenericDaoHibernate<E, PK extends Serializable>
implements IGenericDao<E, PK> {
private Class<E> entityClass;
@Autowired
private SessionFactory sessionFactory;
@SuppressWarnings("unchecked")
public GenericDaoHibernate() {
this.entityClass = (Class<E>) ((ParameterizedType) getClass().
getGenericSuperclass()).getActualTypeArguments()[0];
}
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
protected DataAccessException convertHibernateAccessException(
HibernateException e) {
return SessionFactoryUtils.convertHibernateAccessException(e);
}
public void save(E entity) {
try {
getSession().saveOrUpdate(entity);
} catch (HibernateException e) {
throw convertHibernateAccessException(e);
}
}
@SuppressWarnings("unchecked")
public E find(PK id) throws InstanceNotFoundException {
try {
E entity = (E) getSession().get(entityClass, id);
if (entity == null) {
throw new InstanceNotFoundException(id, entityClass.getName());
}
return entity;
} catch (HibernateException e) {
throw convertHibernateAccessException(e);
}
}
public boolean exists(final PK id) {
try {
return getSession().createCriteria(entityClass).
add(Restrictions.idEq(id)).
setProjection(Projections.id()).
uniqueResult() != null;
} catch (HibernateException e) {
throw convertHibernateAccessException(e);
}
}
public void remove(PK id) throws InstanceNotFoundException {
try {
getSession().delete(find(id));
} catch (HibernateException e) {
throw convertHibernateAccessException(e);
}
}
}

View file

@ -0,0 +1,84 @@
package org.navalplanner.business.common.daos.impl;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.navalplanner.business.common.daos.IGenericDao;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
// FIXME: This class is not currently used. I prefer GenericDaoHibernate.
/**
* All Hibernate DAOs must extend directly from this class. This constraint is
* imposed by the constructor of this class that must infer the type of the
* entity from the concrete DAO declaration.
*
* The class autowires a SessionFactory bean and allows to implement Spring's
* HibernateTemplate-based DAOs. Subclasses access HibernateTemplate by calling
* on getHibernateTemplate() method.
*/
public class GenericDaoHibernateTemplate<E, PK extends Serializable>
implements IGenericDao<E, PK> {
private Class<E> entityClass;
private HibernateTemplate hibernateTemplate;
@SuppressWarnings("unchecked")
public GenericDaoHibernateTemplate() {
this.entityClass = (Class<E>) ((ParameterizedType) getClass().
getGenericSuperclass()).getActualTypeArguments()[0];
}
protected HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}
@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
hibernateTemplate = new HibernateTemplate(sessionFactory);
}
public void save(E entity) {
hibernateTemplate.saveOrUpdate(entity);
}
@SuppressWarnings("unchecked")
public E find(PK id) throws InstanceNotFoundException {
E entity = (E) hibernateTemplate.get(entityClass, id);
if (entity == null) {
throw new InstanceNotFoundException(id, entityClass.getName());
}
return entity;
}
public boolean exists(final PK id) {
return (Boolean) hibernateTemplate.execute(new HibernateCallback() {
public Object doInHibernate(Session session) {
return session.createCriteria(entityClass).
add(Restrictions.idEq(id)).
setProjection(Projections.id()).
uniqueResult() != null;
}
});
}
public void remove(PK id) throws InstanceNotFoundException {
hibernateTemplate.delete(find(id));
}
}

View file

@ -0,0 +1,11 @@
package org.navalplanner.business.common.exceptions;
@SuppressWarnings("serial")
public class DuplicateInstanceException extends InstanceException {
public DuplicateInstanceException(Object key, String className) {
super("Duplicate instance", key, className);
}
}

View file

@ -0,0 +1,27 @@
package org.navalplanner.business.common.exceptions;
@SuppressWarnings("serial")
public abstract class InstanceException extends Exception {
private Object key;
private String className;
protected InstanceException(String specificMessage, Object key,
String className) {
super(specificMessage + " (key = '" + key + "' - className = '" +
className + "')");
this.key = key;
this.className = className;
}
public Object getKey() {
return key;
}
public String getClassName() {
return className;
}
}

View file

@ -0,0 +1,11 @@
package org.navalplanner.business.common.exceptions;
@SuppressWarnings("serial")
public class InstanceNotFoundException extends InstanceException {
public InstanceNotFoundException(Object key, String className) {
super("Instance not found", key, className);
}
}

View file

@ -0,0 +1,6 @@
package org.navalplanner.business.resources.daos;
import org.navalplanner.business.common.daos.IGenericDao;
import org.navalplanner.business.resources.entities.Resource;
public interface IResourceDao extends IGenericDao<Resource, Long> {}

View file

@ -0,0 +1,6 @@
package org.navalplanner.business.resources.daos;
import org.navalplanner.business.common.daos.IGenericDao;
import org.navalplanner.business.resources.entities.ResourceGroup;
public interface IResourceGroupDao extends IGenericDao<ResourceGroup, Long> {}

View file

@ -0,0 +1,6 @@
package org.navalplanner.business.resources.daos;
import org.navalplanner.business.common.daos.IGenericDao;
import org.navalplanner.business.resources.entities.Worker;
public interface IWorkerDao extends IGenericDao<Worker, Long> {}

View file

@ -0,0 +1,46 @@
package org.navalplanner.business.resources.daos;
import org.springframework.beans.factory.annotation.Autowired;
// FIXME: Improve with
// http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom???
// (I think it is not necessary).
/**
* Classes in which dependency injection (DI) is not directly supported by
* Spring (e.g. entities) must use ResourcesDaoRegistry to access
* resource DAOs. For the rest of classes (e.g. services, tests, etc.) Spring
* DI will be a more convenient option.
*/
public final class ResourcesDaoRegistry {
private static ResourcesDaoRegistry instance = new ResourcesDaoRegistry();
@Autowired
private IResourceDao resourceDao;
@Autowired
private IWorkerDao workerDao;
@Autowired
private IResourceGroupDao resourceGroupDao;
private ResourcesDaoRegistry() {}
public static ResourcesDaoRegistry getInstance() {
return instance;
}
public static IResourceDao getResourceDao() {
return getInstance().resourceDao;
}
public static IWorkerDao getWorkerDao() {
return getInstance().workerDao;
}
public static IResourceGroupDao getResourceGroupDao() {
return getInstance().resourceGroupDao;
}
}

View file

@ -0,0 +1,8 @@
package org.navalplanner.business.resources.daos.impl;
import org.navalplanner.business.common.daos.impl.GenericDaoHibernate;
import org.navalplanner.business.resources.daos.IResourceDao;
import org.navalplanner.business.resources.entities.Resource;
public class ResourceDaoHibernate extends GenericDaoHibernate<Resource, Long>
implements IResourceDao {}

View file

@ -0,0 +1,9 @@
package org.navalplanner.business.resources.daos.impl;
import org.navalplanner.business.common.daos.impl.GenericDaoHibernate;
import org.navalplanner.business.resources.daos.IResourceGroupDao;
import org.navalplanner.business.resources.entities.ResourceGroup;
public class ResourceGroupDaoHibernate
extends GenericDaoHibernate<ResourceGroup, Long>
implements IResourceGroupDao {}

View file

@ -0,0 +1,8 @@
package org.navalplanner.business.resources.daos.impl;
import org.navalplanner.business.common.daos.impl.GenericDaoHibernate;
import org.navalplanner.business.resources.daos.IWorkerDao;
import org.navalplanner.business.resources.entities.Worker;
public class WorkerDaoHibernate extends GenericDaoHibernate<Worker, Long>
implements IWorkerDao {}

View file

@ -0,0 +1,59 @@
package org.navalplanner.business.resources.entities;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.resources.daos.ResourcesDaoRegistry;
// FIXME: Alternatively, Resource can be modeled with the style:
// Resource.getParent() & Resource.getChilds(). This way, Resource does not
// depend on ResourceGroup. However, such an option allows combinations not
// semantically correct (e.g. a simple resource, such as Worker, could be the
// child another simple resource, general methods like getChilds() do not make
// sense for simple entities, etc.). In consequence, I prefer the modeling
// option shown below.
public abstract class Resource {
private Long id;
private ResourceGroup resourceGroup;
@SuppressWarnings("unused")
private long version;
public Long getId() {
return id;
}
public ResourceGroup getResourceGroup() {
return resourceGroup;
}
public void setResourceGroup(ResourceGroup resourceGroup) {
this.resourceGroup = resourceGroup;
}
public abstract int getDailyCapacity();
/**
* It removes the resource from the database and updates references.
* The default implementation removes the resource from the resource group
* it belongs to (if it belongs to someone) and from the database. This
* implementation should be valid for simple resources.
*/
public void remove() {
/* Remove from the resource group it belongs to. */
ResourceGroup resourceGroup = getResourceGroup();
if (resourceGroup != null) {
resourceGroup.removeResource(this);
}
/* Remove from the database. */
try {
ResourcesDaoRegistry.getResourceDao().remove(getId());
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,80 @@
package org.navalplanner.business.resources.entities;
import java.util.HashSet;
import java.util.Set;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.resources.daos.ResourcesDaoRegistry;
public class ResourceGroup extends Resource {
private Set<Resource> resources = new HashSet<Resource>();
public Set<Resource> getResources() {
return resources;
}
public void setResources(Set<Resource> resources) {
this.resources = resources;
}
public void addResource(Resource resource) {
/*
* Remove resource from its current resource group (if it belongs to
* one).
*/
if (resource.getResourceGroup() != null) {
resource.getResourceGroup().removeResource(resource);
}
/* Add resource to this resource group. */
resource.setResourceGroup(this);
resources.add(resource);
}
public void addResource(Long resourceId) throws InstanceNotFoundException {
Resource resource =
ResourcesDaoRegistry.getResourceDao().find(resourceId);
addResource(resource);
}
public void removeResource(Resource resource) {
if (resources.contains(resource)) {
resources.remove(resource);
resource.setResourceGroup(null);
}
}
@Override
public int getDailyCapacity() {
int dailyCapacity = 0;
for (Resource r : resources) {
dailyCapacity += r.getDailyCapacity();
}
return dailyCapacity;
}
@Override
public void remove() {
for (Resource r : resources) {
r.setResourceGroup(null);
}
resources.clear();
super.remove();
}
}

View file

@ -0,0 +1,58 @@
package org.navalplanner.business.resources.entities;
public class Worker extends Resource {
private String firstName;
private String surname;
private String nif;
private int dailyHours;
public Worker() {}
public Worker(String firstName, String surname, String nif,
int dailyHours) {
this.firstName = firstName;
this.surname = surname;
this.nif = nif;
this.dailyHours = dailyHours;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getNif() {
return nif;
}
public void setNif(String nif) {
this.nif = nif;
}
public int getDailyHours() {
return dailyHours;
}
public void setDailyHours(int dailyHours) {
this.dailyHours = dailyHours;
}
public int getDailyCapacity() {
return dailyHours;
}
}

View file

@ -0,0 +1,48 @@
package org.navalplanner.business.resources.services;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.resources.entities.Resource;
// FIXME: Define validation approach for creating and updating entities.
// FIXME: Originally we though the interface in a less generic way (e.g.
// createWorker, createResourceGroup, etc.). Now it is completely generic.
// FIXME: The interface must be oriented to detached objects (e.g.
// removeResource(resource) instead of removeResource(resourceId))??? - It
// depends on final requirements.
public interface ResourceService {
/**
* It updates or inserts the resource passed as a parameter. If the
* resource is a composite resource, updating or inserting is cascaded to
* the resources contained in it.
*/
public void saveResource(Resource resource);
public Resource findResource(Long resourceId)
throws InstanceNotFoundException;
/**
* It adds a resource to a resource group. It the resource already belongs
* to a resource group, the resource is moved to the new group.
*/
public void addResourceToResourceGroup(Long resourceId,
Long resourceGroupId) throws InstanceNotFoundException;
// FIXME: Is the following signature better than the previous one??? - I prefer
// the previous one.
// public void addResourceToResourceGroup(Long resourceId, Long resourceGroupId)
// throws ResourceNotFoundException, ResourceGroupNotFoundException;
public int getResourceDailyCapacity(Long resourceId)
throws InstanceNotFoundException;
/**
* It removes a resource. If the resource is a composite resource, the
* resources contained in it are not removed.
*/
public void removeResource(Long resourceId)
throws InstanceNotFoundException;
}

View file

@ -0,0 +1,56 @@
package org.navalplanner.business.resources.services.impl;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.resources.daos.IResourceDao;
import org.navalplanner.business.resources.daos.IResourceGroupDao;
import org.navalplanner.business.resources.entities.Resource;
import org.navalplanner.business.resources.entities.ResourceGroup;
import org.navalplanner.business.resources.services.ResourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
@Transactional
public class ResourceServiceImpl implements ResourceService {
@Autowired
private IResourceDao resourceDao;
@Autowired
private IResourceGroupDao resourceGroupDao;
public void saveResource(Resource resource) {
resourceDao.save(resource);
}
@Transactional(readOnly = true)
public Resource findResource(Long resourceId)
throws InstanceNotFoundException {
return resourceDao.find(resourceId);
}
public void addResourceToResourceGroup(Long resourceId,
Long resourceGroupId) throws InstanceNotFoundException {
ResourceGroup resourceGroup = resourceGroupDao.find(resourceGroupId);
resourceGroup.addResource(resourceId);
}
@Transactional(readOnly = true)
public int getResourceDailyCapacity(Long resourceId)
throws InstanceNotFoundException {
return resourceDao.find(resourceId).getDailyCapacity();
}
public void removeResource(Long resourceId)
throws InstanceNotFoundException {
resourceDao.find(resourceId).remove();
}
}

View file

@ -0,0 +1,13 @@
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">${hibernate.dialect}</property>
<property name="hibernate.show_sql">${hibernate.show_sql}</property>
<property name="hibernate.format_sql">${hibernate.format_sql}</property>
<property name="hibernate.use_sql_comments">${hibernate.use_sql_comments}</property>
<property name="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</property>
</session-factory>
</hibernate-configuration>

View file

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!-- Data source. -->
<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean"
p:jndiName="${dataSource.jndiName}"
p:resourceRef="true" />
<!-- Hibernate Session Factory. -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
p:dataSource-ref="dataSource"
p:configLocation="classpath:/navalplanner-business-hibernate.cfg.xml">
<property name="mappingResources">
<list>
<value>org/navalplanner/business/resources/entities/Resources.hbm.xml</value>
</list>
</property>
</bean>
<!-- Spring Transaction manager -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory"/>
<!-- Enable configuration of transactional behavior based on
annotations -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- For enabling annotation-based configuration (in particular, required
for "@Autowired") -->
<context:annotation-config/>
<!-- ======================== Business Objects ======================== -->
<!-- Resource DAOs and registry -->
<bean id="resourceDao"
class="org.navalplanner.business.resources.daos.impl.ResourceDaoHibernate"/>
<bean id="workerDao"
class="org.navalplanner.business.resources.daos.impl.WorkerDaoHibernate"/>
<bean id="resourceGroupDao"
class="org.navalplanner.business.resources.daos.impl.ResourceGroupDaoHibernate"/>
<bean id="resourcesDaoRegistry"
class="org.navalplanner.business.resources.daos.ResourcesDaoRegistry"
factory-method="getInstance"/>
<!-- Service layer -->
<bean id="resourceService"
class="org.navalplanner.business.resources.services.impl.ResourceServiceImpl"/>
</beans>

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class name="org.navalplanner.business.resources.entities.Resource">
<id name="id" access="field">
<generator class="native"/>
</id>
<!-- IMPORTANT: type="long" must be specified (otherwise, Hibernate
infers type="integer". -->
<version name="version" type="long" access="field"/>
<many-to-one name="resourceGroup"
column="RESOURCE_GROUP_ID"
class="org.navalplanner.business.resources.entities.ResourceGroup"/>
<joined-subclass
name="org.navalplanner.business.resources.entities.Worker">
<key column="WORKER_ID"/>
<property name="firstName"/>
<property name="surname"/>
<property name="nif"/>
<property name="dailyHours"/>
</joined-subclass>
<joined-subclass
name="org.navalplanner.business.resources.entities.ResourceGroup">
<key column="RESOURCE_GROUP_ID"/>
<set name="resources" inverse="true" cascade="save-update">
<key column="RESOURCE_GROUP_ID"/>
<one-to-many
class="org.navalplanner.business.resources.entities.Resource"/>
</set>
</joined-subclass>
</class>
</hibernate-mapping>

View file

@ -0,0 +1,10 @@
package org.navalplanner.business.test;
public class BusinessGlobalNames {
public final static String BUSINESS_SPRING_CONFIG_TEST_FILE =
"classpath:/navalplanner-business-spring-config-test.xml";
private BusinessGlobalNames () {}
}

View file

@ -0,0 +1,173 @@
package org.navalplanner.business.test.resources.services;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE;
import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.resources.daos.IResourceDao;
import org.navalplanner.business.resources.entities.ResourceGroup;
import org.navalplanner.business.resources.entities.Worker;
import org.navalplanner.business.resources.services.ResourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={BUSINESS_SPRING_CONFIG_FILE,
BUSINESS_SPRING_CONFIG_TEST_FILE})
@Transactional
public class ResourceServiceTest {
@Autowired
private ResourceService resourceService;
@Autowired
private IResourceDao resourceDao;
@Test
public void testAddResourceToResourceGroup()
throws InstanceNotFoundException {
/* Two workers. One of them belongs to a resource group. */
Worker worker1 = new Worker("worker-1", "worker-1-surname",
"11111111A", 8);
Worker worker2 = new Worker("worker-2", "worker-2-surname",
"22222222B", 7);
ResourceGroup resourceGroup1 = new ResourceGroup();
resourceGroup1.addResource(worker1);
resourceService.saveResource(resourceGroup1); // worker1 is also saved.
resourceService.saveResource(worker2);
/* A resource group. */
ResourceGroup resourceGroup2 = new ResourceGroup();
resourceService.saveResource(resourceGroup2);
/* Add workers to resource group. */
resourceService.addResourceToResourceGroup(worker1.getId(),
resourceGroup2.getId());
resourceService.addResourceToResourceGroup(worker2.getId(),
resourceGroup2.getId());
/* Check resource group. */
ResourceGroup resourceGroup = (ResourceGroup)
resourceService.findResource(resourceGroup2.getId());
assertEquals(2, resourceGroup.getResources().size());
assertTrue(resourceGroup.getResources().contains(worker1));
assertTrue(resourceGroup.getResources().contains(worker2));
/* Check worker1 is no longer in group 1. */
assertFalse(resourceGroup1.getResources().contains(worker1));
}
@Test
public void testGetResourceDailyCapacity()
throws InstanceNotFoundException {
/* Three workers. */
Worker worker1 = new Worker("worker-1", "worker-1-surname",
"11111111A", 8);
Worker worker2 = new Worker("worker-2", "worker-2-surname",
"22222222B", 7);
Worker worker3 = new Worker("worker-3", "worker-3-surname",
"33333333C", 6);
/* A group of two workers. */
ResourceGroup resourceGroup1 = new ResourceGroup();
Worker worker4 = new Worker("worker-4", "worker-4-surname",
"44444444D", 5);
Worker worker5 = new Worker("worker-5", "worker-5-surname",
"55555555E", 4);
resourceGroup1.addResource(worker4);
resourceGroup1.addResource(worker5);
/*
* A complex group containing the first three workers and a group with
* the last two workers.
*/
ResourceGroup resourceGroup2 = new ResourceGroup();
resourceGroup2.addResource(worker1);
resourceGroup2.addResource(worker2);
resourceGroup2.addResource(worker3);
resourceGroup2.addResource(resourceGroup1);
/* Calculate total daily capacity. */
int totalDailyCapacity =
worker1.getDailyCapacity() + worker2.getDailyCapacity() +
worker3.getDailyCapacity() + worker4.getDailyCapacity() +
worker5.getDailyCapacity();
/* Save the second group (and in consequence all resources). */
resourceService.saveResource(resourceGroup2);
/* Test ResourceService's getResourceDailyCapacity. */
int resourceGroupDailyCapacity =
resourceService.getResourceDailyCapacity(resourceGroup2.getId());
assertEquals(totalDailyCapacity, resourceGroupDailyCapacity);
}
@Test
public void testRemoveResource() throws InstanceNotFoundException {
/* A group of three workers. */
ResourceGroup resourceGroup = new ResourceGroup();
Worker worker1 = new Worker("worker-1", "worker-2-surname",
"11111111A", 8);
Worker worker2 = new Worker("worker-2", "worker-3-surname",
"22222222B", 6);
Worker worker3 = new Worker("worker-3", "worker-3-surname",
"33333333C", 4);
resourceGroup.addResource(worker1);
resourceGroup.addResource(worker2);
resourceGroup.addResource(worker3);
resourceService.saveResource(resourceGroup);
/* Remove worker 3. */
resourceService.removeResource(worker3.getId());
/* Check worker 3 does not exist. */
assertFalse(resourceDao.exists(worker3.getId()));
/*
* Check worker 3 is not in resource group and the other workers
* are still in the group.
*/
assertFalse(resourceGroup.getResources().contains(worker3));
assertTrue(resourceGroup.getResources().contains(worker1));
assertTrue(resourceGroup.getResources().contains(worker2));
/* Remove the group. */
resourceService.removeResource(resourceGroup.getId());
/* Check the resource group does not exist. */
assertFalse(resourceDao.exists(resourceGroup.getId()));
/* Check workers still exist. */
assertTrue(resourceDao.exists(worker1.getId()));
assertTrue(resourceDao.exists(worker2.getId()));
/* Check workers do not belong to any resource group. */
assertNull(worker1.getResourceGroup());
assertNull(worker2.getResourceGroup());
/* Remove workers. */
resourceService.removeResource(worker1.getId());
resourceService.removeResource(worker2.getId());
/* Check workers do not exist. */
assertFalse(resourceDao.exists(worker1.getId()));
assertFalse(resourceDao.exists(worker2.getId()));
}
}

View file

@ -0,0 +1,13 @@
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">${hibernate.dialect}</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.use_sql_comments">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!-- Data source -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="${jdbcDriver.className}"
p:url="${testDataSource.url}" p:username="${testDataSource.user}"
p:password="${testDataSource.password}"/>
<!-- Hibernate Session Factory. -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
p:dataSource-ref="dataSource"
p:configLocation="classpath:/navalplanner-business-hibernate-test.cfg.xml">
<!-- FIXME: ideally this chunk of XML should be only in
src/main/resources/navalplanner-business-spring-config.xml. -->
<property name="mappingResources">
<list>
<value>org/navalplanner/business/resources/entities/Resources.hbm.xml</value>
</list>
</property>
</bean>
</beans>

View file

@ -0,0 +1,36 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.navalplanner</groupId>
<artifactId>navalplanner</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>navalplanner-gantt-zk</artifactId>
<packaging>jar</packaging>
<name>Naval Planner ZK Components Module</name>
<dependencies>
<!-- ZK -->
<dependency>
<groupId>org.zkoss.zk</groupId>
<artifactId>zul</artifactId>
</dependency>
<dependency>
<groupId>org.zkoss.zk</groupId>
<artifactId>zkplus</artifactId>
</dependency>
<dependency>
<groupId>org.zkoss.zk</groupId>
<artifactId>zk</artifactId>
</dependency>
<!-- Commons Logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,15 @@
package org.zkoss.ganttz;
import java.util.Date;
public interface DatesMapper {
int toPixels(Date date);
Date toDate(int pixel);
int toPixels(long milliseconds);
long toMilliseconds(int pixels);
}

View file

@ -0,0 +1,44 @@
/**
*
*/
package org.zkoss.ganttz;
import java.util.Date;
import org.zkoss.ganttz.util.Interval;
public class DatesMapperOnInterval implements DatesMapper {
private final int horizontalSize;
private final Interval stubInterval;
private long millisecondsPerPixel;
public DatesMapperOnInterval(int horizontalSize, Interval stubInterval) {
this.horizontalSize = horizontalSize;
this.stubInterval = stubInterval;
this.millisecondsPerPixel = stubInterval.getLengthBetween()
/ horizontalSize;
}
@Override
public Date toDate(int pixels) {
return new Date(stubInterval.getStart().getTime()
+ millisecondsPerPixel * pixels);
}
@Override
public int toPixels(Date date) {
double proportion = stubInterval.getProportion(date);
return (int) (horizontalSize * proportion);
}
@Override
public int toPixels(long milliseconds) {
Date date = new Date(stubInterval.getStart().getTime() + milliseconds);
return this.toPixels(date);
}
@Override
public long toMilliseconds(int pixels) {
return millisecondsPerPixel * pixels;
}
}

View file

@ -0,0 +1,99 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.zkoss.ganttz;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import org.zkoss.zk.au.out.AuInvoke;
import org.zkoss.zk.ui.ext.AfterCompose;
import org.zkoss.zul.impl.XulElement;
/**
*
* @author Francisco Javier Moran Rúa
*
*/
public class Dependency extends XulElement implements AfterCompose {
private Task source;
public Task getSource() {
return source;
}
public Task getDestination() {
return destination;
}
private Task destination;
public Dependency() {
}
public Dependency(Task source, Task destination) {
this();
if (source == null)
throw new IllegalArgumentException("source cannot be null");
if (destination == null)
throw new IllegalArgumentException("destination cannot be null");
this.source = source;
this.destination = destination;
}
@Override
public void afterCompose() {
PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
redrawDependency();
}
};
this.source.getTaskBean().addPropertyChangeListener(listener);
this.destination.getTaskBean().addPropertyChangeListener(listener);
}
/**
* @return the idTaskOrig
*/
public String getIdTaskOrig() {
return source.getUuid();
}
public void setIdTaskOrig(String idTaskOrig) {
this.source = findTask(idTaskOrig);
}
private Task findTask(String idTaskOrig) {
return (Task) getFellow(idTaskOrig);
}
/**
* @return the idTaskEnd
*/
public String getIdTaskEnd() {
return destination.getUuid();
}
public void setIdTaskEnd(String idTaskEnd) {
this.destination = findTask(idTaskEnd);
}
public void zoomChanged() {
redrawDependency();
}
public void redrawDependency() {
response("zoomChanged", new AuInvoke(this, "draw"));
}
public boolean contains(Task task) {
return getSource().equals(task) || getDestination().equals(task);
}
}

View file

@ -0,0 +1,9 @@
/**
*
*/
package org.zkoss.ganttz;
public interface DependencyAddedListener {
public void dependenceAdded(Dependency dependency);
}

View file

@ -0,0 +1,122 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.zkoss.ganttz;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.zkoss.ganttz.util.MenuBuilder;
import org.zkoss.ganttz.util.MenuBuilder.ItemAction;
import org.zkoss.ganttz.util.zoom.ZoomLevel;
import org.zkoss.ganttz.util.zoom.ZoomLevelChangedListener;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.ext.AfterCompose;
import org.zkoss.zul.Menupopup;
import org.zkoss.zul.impl.XulElement;
/**
*
* @author Francisco Javier Moran Rúa
*
*/
public class DependencyList extends XulElement implements AfterCompose {
private static final Log LOG = LogFactory.getLog(DependencyList.class);
private TaskRemovedListener taskRemovedListener;
private ZoomLevelChangedListener listener;
public DependencyList() {
}
private List<Dependency> getDependencies() {
List<Object> children = getChildren();
return Planner.findComponentsOfType(Dependency.class, children);
}
void addDependency(Dependency dependency) {
appendChild(dependency);
addContextMenu(dependency);
}
private void addContextMenu(Dependency dependency) {
dependency.setContext(getContextMenu());
}
private GanttPanel getGanttPanel() {
return (GanttPanel) getParent();
}
@Override
public void afterCompose() {
if (listener == null) {
listener = new ZoomLevelChangedListener() {
@Override
public void zoomLevelChanged(ZoomLevel detailLevel) {
for (Dependency dependency : getDependencies()) {
dependency.zoomChanged();
}
}
};
getTimeTracker().addZoomListener(listener);
}
if (taskRemovedListener == null) {
taskRemovedListener = new TaskRemovedListener() {
@Override
public void taskRemoved(Task taskRemoved) {
for (Dependency dependency : DependencyList.this
.getDependencies()) {
if (dependency.contains(taskRemoved)) {
dependency.detach();
}
}
}
};
getGanttPanel().getTaskList().addTaskRemovedListener(
taskRemovedListener);
}
addContextMenu();
}
private void addContextMenu() {
for (Dependency dependency : getDependencies()) {
addContextMenu(dependency);
}
}
private Menupopup contextMenu;
private Dependency dependencyForContextMenu = null;
private Menupopup getContextMenu() {
if (contextMenu == null) {
contextMenu = MenuBuilder.on(getPage(), getDependencies()).item(
"Erase", new ItemAction<Dependency>() {
@Override
public void onEvent(Dependency choosen, Event event) {
removeChild(choosen);
}
}).create();
}
return contextMenu;
}
private TimeTracker getTimeTracker() {
return getGanttPanel().getTimeTracker();
}
public void redrawDependencies() {
for (Dependency dependency : getDependencies()) {
dependency.redrawDependency();
}
}
}

View file

@ -0,0 +1,29 @@
package org.zkoss.ganttz;
import java.util.List;
import org.zkoss.zul.impl.XulElement;
public class GanttPanel extends XulElement {
public TimeTracker getTimeTracker() {
List<Object> children = getChildren();
return Planner.findComponentsOfType(TimeTracker.class, children).get(0);
}
public TaskList getTaskList() {
List<Object> children = getChildren();
return Planner.findComponentsOfType(TaskList.class, children).get(0);
}
public DependencyList getDependencyList() {
List<Object> children = getChildren();
return Planner.findComponentsOfType(DependencyList.class, children)
.get(0);
}
public Planner getPlanner() {
return (Planner) getParent();
}
}

View file

@ -0,0 +1,41 @@
package org.zkoss.ganttz;
import java.util.Date;
import java.util.UUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.HtmlMacroComponent;
public class ListDetails extends HtmlMacroComponent {
private static Log LOG = LogFactory.getLog(ListDetails.class);
public ListDetails() {
LOG.info("constructing list details");
}
Planner getPlanner() {
return (Planner) getParent();
}
public void addTask() {
TaskDetail taskDetail = new TaskDetail();
String newId = UUID.randomUUID().toString();
taskDetail.setTaskId(newId);
taskDetail.setDynamicProperty("start", TaskDetail.format(new Date()));
taskDetail.setDynamicProperty("length", "30 days");
taskDetail.setDynamicProperty("taskName", Labels
.getLabel("task.new_task_name"));
Component insertionPoint = getFellow("insertionPoint");
taskDetail.setParent(insertionPoint);
taskDetail.afterCompose();
Task task = new Task();
getPlanner().addTask(task);
task.setColor("yellow");
task.setId(newId);
}
}

View file

@ -0,0 +1,87 @@
package org.zkoss.ganttz;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.zkoss.zk.ui.ext.AfterCompose;
import org.zkoss.zul.impl.XulElement;
public class Planner extends XulElement implements AfterCompose {
private DependencyAddedListener dependencyAddedListener;
private Map<String, TaskBean> tasksById = new HashMap<String, TaskBean>();
public Planner() {
}
private TaskList getTaskList() {
List<Object> children = findOneComponentOfType(GanttPanel.class)
.getChildren();
return Planner.findComponentsOfType(TaskList.class, children).get(0);
}
private <T> T findOneComponentOfType(Class<T> type) {
List<T> result = findComponentsOfType(type, getChildren());
if (result.isEmpty()) {
throw new RuntimeException("it should have found a "
+ type.getSimpleName() + " in "
+ Planner.class.getSimpleName());
}
return result.get(0);
}
void publish(String taskId, TaskBean task) {
if (taskId == null)
throw new IllegalArgumentException("taskId cannot be null");
if (task == null)
throw new IllegalArgumentException("task cannot be null");
if (tasksById.containsKey(taskId))
throw new IllegalArgumentException("task with id " + taskId
+ " is already in " + tasksById);
tasksById.put(taskId, task);
}
TaskBean retrieve(String taskId) {
return tasksById.get(taskId);
}
public static <T> List<T> findComponentsOfType(Class<T> type,
List<? extends Object> children) {
ArrayList<T> result = new ArrayList<T>();
for (Object child : children) {
if (type.isInstance(child)) {
result.add(type.cast(child));
}
}
return result;
}
private GanttPanel getGanntPanel() {
return findOneComponentOfType(GanttPanel.class);
}
private DependencyList getDependencyList() {
List<Object> children = getGanntPanel().getChildren();
return findComponentsOfType(DependencyList.class, children).get(0);
}
@Override
public void afterCompose() {
TaskList taskList = getTaskList();
dependencyAddedListener = new DependencyAddedListener() {
@Override
public void dependenceAdded(Dependency dependency) {
getDependencyList().addDependency(dependency);
}
};
taskList.addDependencyListener(dependencyAddedListener);
}
public void addTask(Task task) {
getTaskList().addTask(task);
}
}

View file

@ -0,0 +1,298 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.zkoss.ganttz;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.zkoss.lang.Objects;
import org.zkoss.xml.HTMLs;
import org.zkoss.zk.au.AuRequest;
import org.zkoss.zk.au.Command;
import org.zkoss.zk.au.ComponentCommand;
import org.zkoss.zk.au.out.AuInvoke;
import org.zkoss.zk.mesg.MZk;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.UiException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Div;
/**
*
* @author javi
*/
public class Task extends Div {
private static Pattern pixelsSpecificationPattern = Pattern
.compile("\\s*(\\d+)px\\s*;?\\s*");
private static int stripPx(String pixels) {
Matcher matcher = pixelsSpecificationPattern.matcher(pixels);
if (!matcher.matches())
throw new IllegalArgumentException("pixels " + pixels
+ " is not valid. It must be "
+ pixelsSpecificationPattern.pattern());
return Integer.valueOf(matcher.group(1));
}
private static Command _updatecmd = new ComponentCommand(
"onUpdatePosition", 0) {
protected void process(AuRequest request) {
final Task ta = (Task) request.getComponent();
if (ta == null) {
throw new UiException(MZk.ILLEGAL_REQUEST_COMPONENT_REQUIRED,
this);
}
String[] requestData = request.getData();
if ((requestData != null) && (requestData.length != 2)) {
throw new UiException(MZk.ILLEGAL_REQUEST_WRONG_DATA,
new Object[] { Objects.toString(requestData), this });
} else {
ta.doUpdatePosition(requestData[0], requestData[1]);
Events.postEvent(new Event(getId(), ta, request.getData()));
}
}
};
private static Command _updatewidthcmd = new ComponentCommand(
"onUpdateWidth", 0) {
protected void process(AuRequest request) {
final Task ta = (Task) request.getComponent();
if (ta == null) {
throw new UiException(MZk.ILLEGAL_REQUEST_COMPONENT_REQUIRED,
this);
}
String[] requestData = request.getData();
if ((requestData != null) && (requestData.length != 1)) {
throw new UiException(MZk.ILLEGAL_REQUEST_WRONG_DATA,
new Object[] { Objects.toString(requestData), this });
} else {
ta.doUpdateSize(requestData[0]);
Events.postEvent(new Event(getId(), ta, request.getData()));
}
}
};
private static Command _adddependencycmd = new ComponentCommand(
"onAddDependency", 0) {
protected void process(AuRequest request) {
final Task task = (Task) request.getComponent();
if (task == null) {
throw new UiException(MZk.ILLEGAL_REQUEST_COMPONENT_REQUIRED,
this);
}
String[] requestData = request.getData();
if ((requestData != null) && (requestData.length != 1)) {
throw new UiException(MZk.ILLEGAL_REQUEST_WRONG_DATA,
new Object[] { Objects.toString(requestData), this });
} else {
task.doAddDependency(requestData[0]);
Events.postEvent(new Event(getId(), task, request.getData()));
}
}
};
public Task() {
setHeight("20px"); /* Initial constant for standard task height */
setContext("idContextMenuTaskAssigment");
}
private String _color;
private List<WeakReference<DependencyAddedListener>> dependencyListeners = new LinkedList<WeakReference<DependencyAddedListener>>();
private TaskBean taskBean;
public TaskBean getTaskBean() {
return taskBean;
}
public String getTaskName() {
return taskBean.getName();
}
@Override
public void setId(String id) {
super.setId(id);
if (taskBean != null)
throw new IllegalStateException("taskBean already set");
taskBean = getPlanner().retrieve(id);
updateProperties();
taskBean.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
updateProperties();
}
});
}
public String getLength() {
return null;
}
public void addDependencyListener(DependencyAddedListener listener) {
dependencyListeners.add(new WeakReference<DependencyAddedListener>(
listener));
}
private void fireDependenceAdded(Dependency dependency) {
ArrayList<DependencyAddedListener> active = new ArrayList<DependencyAddedListener>();
synchronized (this) {
ListIterator<WeakReference<DependencyAddedListener>> iterator = dependencyListeners
.listIterator();
while (iterator.hasNext()) {
WeakReference<DependencyAddedListener> next = iterator.next();
DependencyAddedListener listener = next.get();
if (listener == null) {
iterator.remove();
} else {
active.add(listener);
}
}
}
for (DependencyAddedListener listener : active) {
listener.dependenceAdded(dependency);
}
}
public Command getCommand(String cmdId) {
Command c = null;
if ("updatePosition".equals(cmdId))
c = _updatecmd;
else if ("updateSize".equals(cmdId))
c = _updatewidthcmd;
else if ("addDependency".equals(cmdId))
c = _adddependencycmd;
return c;
}
// Command action to do
void doUpdatePosition(String leftX, String topY) {
System.out.println("leftX:" + getLeft() + "newLeft:" + leftX);
setTop(topY);
this.taskBean.setBeginDate(getMapper().toDate(stripPx(leftX)));
}
void doUpdateSize(String size) {
System.out.println("size:" + getWidth() + "newWidth:" + size);
int pixels = stripPx(size);
this.taskBean.setLengthMilliseconds(getMapper().toMilliseconds(pixels));
}
void doAddDependency(String dependencyId) {
Dependency dependency = new Dependency(this,
((Task) getFellow(dependencyId)));
fireDependenceAdded(dependency);
}
public String getColor() {
return _color;
}
public void setColor(String color) {
if ((color != null) && (color.length() == 0)) {
color = null;
}
if (!Objects.equals(_color, color)) {
_color = color;
}
}
/*
* We override the method of getRealStyle to put the color property as part
* of the style
*/
protected String getRealStyle() {
final StringBuffer sb = new StringBuffer(super.getRealStyle());
if (getColor() != null) {
HTMLs.appendStyle(sb, "background-color", getColor());
}
HTMLs.appendStyle(sb, "position", "absolute");
return sb.toString();
}
/*
* We send a response to the client to create the arrow we are going to use
* to create the dependency
*/
public void addDependency() {
response("depkey", new AuInvoke(this, "addDependency"));
}
private DatesMapper getMapper() {
return getTaskList().getMapper();
}
public TaskList getTaskList() {
return (TaskList) getParent();
}
public Planner getPlanner() {
return getTaskList().getPlanner();
}
@Override
public void setParent(Component parent) {
if (parent != null && !(parent instanceof TaskList))
throw new UiException("Unsupported parent for rows: " + parent);
super.setParent(parent);
}
public void zoomChanged() {
updateProperties();
}
private void updateProperties() {
setLeft(getMapper().toPixels(this.taskBean.getBeginDate()) + "px");
setWidth(getMapper().toPixels(this.taskBean.getLengthMilliseconds())
+ "px");
smartUpdate("name", this.taskBean.getName());
}
public void remove() {
getTaskList().removeTask(this);
}
}

View file

@ -0,0 +1,29 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.zkoss.ganttz;
import org.zkoss.zk.au.AuRequest;
import org.zkoss.zk.au.Command;
/**
*
* @author Francisco Javier Moran Rúa
*
*/
public class TaskAssigmentMoveCommand extends Command {
public TaskAssigmentMoveCommand(String event,int flags) {
super(event,flags);
}
protected void process(AuRequest request) {
System.out.println("Processing command");
}
}

View file

@ -0,0 +1,97 @@
package org.zkoss.ganttz;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Date;
public class TaskBean {
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
this);
private String name;
private Date beginDate = null;
private long lengthMilliseconds = 0;
private String notes;
public TaskBean() {
}
public TaskBean(String name, Date beginDate, long lengthMilliseconds) {
if (name == null)
throw new IllegalArgumentException("name cannot be null");
if (beginDate == null)
throw new IllegalArgumentException("beginDate cannot be null");
if (lengthMilliseconds < 0)
throw new IllegalArgumentException(
"length in milliseconds must be positive. Instead it is "
+ lengthMilliseconds);
this.name = name;
this.beginDate = beginDate;
this.lengthMilliseconds = lengthMilliseconds;
}
public String getName() {
return name;
}
public void setName(String name) {
String previousValue = this.name;
this.name = name;
propertyChangeSupport.firePropertyChange("name", previousValue,
this.name);
}
public void setBeginDate(Date beginDate) {
Date previousValue = this.beginDate;
this.beginDate = beginDate;
propertyChangeSupport.firePropertyChange("beginDate", previousValue,
this.beginDate);
}
public Date getBeginDate() {
return new Date(beginDate.getTime());
}
public void setLengthMilliseconds(long lengthMilliseconds) {
long previousValue = this.lengthMilliseconds;
this.lengthMilliseconds = lengthMilliseconds;
propertyChangeSupport.firePropertyChange("lengthMilliseconds",
previousValue, this.lengthMilliseconds);
}
public long getLengthMilliseconds() {
return lengthMilliseconds;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
this.propertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
this.propertyChangeSupport.removePropertyChangeListener(listener);
}
public Date getEndDate() {
return new Date(beginDate.getTime() + lengthMilliseconds);
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
String previousValue = this.notes;
this.notes = notes;
propertyChangeSupport.firePropertyChange("notes", previousValue,
this.notes);
}
public void setEndDate(Date value) {
setLengthMilliseconds(value.getTime() - beginDate.getTime());
}
}

View file

@ -0,0 +1,174 @@
package org.zkoss.ganttz;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.zkoss.zk.ui.AbstractComponent;
import org.zkoss.zk.ui.HtmlMacroComponent;
import org.zkoss.zk.ui.ext.AfterCompose;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Textbox;
public class TaskDetail extends HtmlMacroComponent implements AfterCompose {
private static long parseLength(String length) {
return LengthType.getTimeInMilliseconds(length);
}
private static Date parseStartDate(String start) {
try {
return dateFormat.parse(start);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
static String format(Date date) {
return dateFormat.format(date);
}
private static DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
private static final Log LOG = LogFactory.getLog(TaskDetail.class);
private static Pattern lengthPattern = Pattern
.compile("\\s*(\\d+)\\s*(\\w+)\\s*");
private enum LengthType {
HOUR(3600, "h", "hour", "hora", "horas"), DAYS(3600 * 24, "day", "dia",
"dias", "días", "día", "days");
private final long milliseconds;
private Set<String> set;
private LengthType(int seconds, String... sufixes) {
milliseconds = seconds * 1000;
set = new HashSet<String>(Arrays.asList(sufixes));
}
public static long getTimeInMilliseconds(String spec) {
Matcher matcher = lengthPattern.matcher(spec);
if (!matcher.matches())
throw new IllegalArgumentException("spec " + spec
+ " is not matched by " + lengthPattern.pattern());
long number = Integer.parseInt(matcher.group(1));
String specifier = matcher.group(2).toLowerCase();
for (LengthType type : LengthType.values()) {
if (type.set.contains(specifier)) {
return number * type.milliseconds;
}
}
throw new IllegalArgumentException(specifier + " not found");
}
}
private String taskId;
private TaskBean taskBean;
public TaskBean getTaskBean() {
return taskBean;
}
private Textbox nameBox;
public Textbox getNameBox() {
return nameBox;
}
public void setNameBox(Textbox nameBox) {
this.nameBox = nameBox;
}
public Datebox getStartDateBox() {
return startDateBox;
}
public void setStartDateBox(Datebox startDateBox) {
this.startDateBox = startDateBox;
this.startDateBox.setCompact(true);
this.startDateBox.setFormat("dd/MM/yyyy");
}
public Datebox getEndDateBox() {
return endDateBox;
}
public void setEndDateBox(Datebox endDateBox) {
this.endDateBox = endDateBox;
this.endDateBox.setFormat("dd/MM/yyyy");
}
private Datebox startDateBox;
private Datebox endDateBox;
public String getTaskId() {
return taskId;
}
public void setTaskId(String taskId) {
this.taskId = taskId;
}
public TaskDetail() {
LOG.info("Detail component constructor");
}
public TaskBean getData() {
return taskBean;
}
private Planner getPlanner() {
AbstractComponent parent = (AbstractComponent) getParent();
while (!(parent instanceof ListDetails)) {
parent = (AbstractComponent) parent.getParent();
}
return ((ListDetails) parent).getPlanner();
}
@Override
public void afterCompose() {
super.afterCompose();
taskBean = new TaskBean((String) getDynamicProperty("taskName"),
parseStartDate((String) getDynamicProperty("start")),
parseLength((String) getDynamicProperty("length")));
getPlanner().publish(taskId, taskBean);
updateComponents();
taskBean.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
updateComponents();
}
});
}
public void updateBean() {
if (getEndDateBox().getValue().before(getStartDateBox().getValue())) {
updateComponents();
return;
}
taskBean.setName(getNameBox().getValue());
taskBean.setBeginDate(getStartDateBox().getValue());
taskBean.setEndDate(getEndDateBox().getValue());
}
private void updateComponents() {
getNameBox().setValue(taskBean.getName());
getStartDateBox().setValue(taskBean.getBeginDate());
getEndDateBox().setValue(taskBean.getEndDate());
}
}

View file

@ -0,0 +1,92 @@
package org.zkoss.ganttz;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.Window;
public class TaskEditFormComposer extends GenericForwardComposer {
public TaskEditFormComposer() {
}
private Window window;
private TaskBean currentTask;
private Textbox name;
private Datebox startDateBox;
private Datebox endDateBox;
private Textbox notes;
private PropertyChangeListener propertyChangeListener;
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
window = (Window) comp;
window.setVisible(false);
}
public void showEditFormFor(Task task) {
cleanListener();
this.currentTask = task.getTaskBean();
window.doPopup();
propertyChangeListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (window.isVisible()) {
updateComponentValuesForTask(currentTask);
}
}
};
currentTask.addPropertyChangeListener(propertyChangeListener);
updateComponentValuesForTask(currentTask);
}
private void cleanListener() {
if (this.currentTask != null) {
this.currentTask
.removePropertyChangeListener(propertyChangeListener);
}
}
private void updateComponentValuesForTask(TaskBean currentTask) {
window.setTitle(currentTask.getName());
name.setValue(currentTask.getName());
startDateBox.setValue(currentTask.getBeginDate());
endDateBox.setValue(currentTask.getEndDate());
notes.setValue(currentTask.getNotes());
}
public void onChange$name(Event event) {
currentTask.setName(name.getValue());
}
public void onChange$startDateBox(Event event) {
currentTask.setBeginDate(startDateBox.getValue());
}
public void onChange$endDateBox(Event event) {
currentTask.setEndDate(endDateBox.getValue());
}
public void onChange$notes(Event event) {
currentTask.setNotes(notes.getValue());
}
public void onClick$ok(Event event) {
window.setVisible(false);
cleanListener();
}
}

View file

@ -0,0 +1,212 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.zkoss.ganttz;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import org.zkoss.ganttz.util.MenuBuilder;
import org.zkoss.ganttz.util.WeakReferencedListeners;
import org.zkoss.ganttz.util.MenuBuilder.ItemAction;
import org.zkoss.ganttz.util.WeakReferencedListeners.ListenerNotification;
import org.zkoss.ganttz.util.zoom.ZoomLevel;
import org.zkoss.ganttz.util.zoom.ZoomLevelChangedListener;
import org.zkoss.zk.au.out.AuInvoke;
import org.zkoss.zk.ui.AbstractComponent;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.ext.AfterCompose;
import org.zkoss.zul.Menupopup;
import org.zkoss.zul.impl.XulElement;
/**
*
* @author Francisco Javier Moran Rúa
*
*/
public class TaskList extends XulElement implements AfterCompose {
private static final int HEIGHT_PER_ROW = 40;
private List<WeakReference<DependencyAddedListener>> listeners = new LinkedList<WeakReference<DependencyAddedListener>>();
private ZoomLevelChangedListener zoomLevelChangedListener;
private final WeakReferencedListeners<TaskRemovedListener> taskRemovedListeners = WeakReferencedListeners
.create();
private Menupopup contextMenu;
private TaskEditFormComposer taskEditFormComposer = new TaskEditFormComposer();
public synchronized void addTask(Task task) {
task.setParent(this);
invalidate();
getDependencyList().invalidate();
addContextMenu(task);
addListenerForTaskEditForm(task);
ListIterator<WeakReference<DependencyAddedListener>> iterator = listeners
.listIterator();
while (iterator.hasNext()) {
DependencyAddedListener listener = iterator.next().get();
if (listener != null) {
task.addDependencyListener(listener);
} else {
iterator.remove();
}
}
}
private DependencyList getDependencyList() {
return getGanttPanel().getDependencyList();
}
private void addListenersForTaskEditForm() {
for (Task task : getTasks()) {
addListenerForTaskEditForm(task);
}
}
private void addListenerForTaskEditForm(final Task task) {
task.addEventListener("onDoubleClick", new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
taskEditFormComposer.showEditFormFor(task);
}
});
}
private void addContextMenu(final Task task) {
task.addEventListener("onRightClick", new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
getContextMenuForTasks().open(task);
}
});
}
private void addContextMenu() {
for (Task task : getTasks()) {
addContextMenu(task);
}
}
public void addRemoveListener(TaskRemovedListener listener) {
taskRemovedListeners.addListener(listener);
}
public void removeTask(final Task task) {
removeChild(task);
task.detach();
taskRemovedListeners
.fireEvent(new ListenerNotification<TaskRemovedListener>() {
@Override
public void doNotify(TaskRemovedListener listener) {
listener.taskRemoved(task);
}
});
}
@Override
public String getHeight() {
return getTasksNumber() * HEIGHT_PER_ROW + "px";
}
public String getSameHeightElementId() {
TimeTracker timeTracker = getTimeTracker();
AbstractComponent fakeRow = timeTracker.getFakeRow();
return fakeRow.getUuid();
}
private TimeTracker getTimeTracker() {
return (getGanttPanel()).getTimeTracker();
}
DatesMapper getMapper() {
return getTimeTracker().getMapper();
}
private List<Task> getTasks() {
ArrayList<Task> result = new ArrayList<Task>();
for (Object child : getChildren()) {
if (child instanceof Task) {
result.add((Task) child);
}
}
return result;
}
private int getTasksNumber() {
return getTasks().size();
}
public void addTaskRemovedListener(TaskRemovedListener taskRemovedListener) {
taskRemovedListeners.addListener(taskRemovedListener);
}
public void addDependencyListener(DependencyAddedListener listener) {
listeners.add(new WeakReference<DependencyAddedListener>(listener));
for (Task task : getTasks()) {
task.addDependencyListener(listener);
}
}
@Override
public void afterCompose() {
if (zoomLevelChangedListener == null) {
zoomLevelChangedListener = new ZoomLevelChangedListener() {
@Override
public void zoomLevelChanged(ZoomLevel detailLevel) {
for (Task task : getTasks()) {
task.zoomChanged();
}
response("adjust_height", new AuInvoke(TaskList.this,
"adjust_height"));
}
};
getTimeTracker().addZoomListener(zoomLevelChangedListener);
}
addListenersForTaskEditForm();
addContextMenu();
}
private Menupopup getContextMenuForTasks() {
if (contextMenu == null) {
contextMenu = MenuBuilder.on(getPage(), getTasks()).item(
"Add Dependency", new ItemAction<Task>() {
@Override
public void onEvent(Task choosen, Event event) {
choosen.addDependency();
}
}).item("Erase", new ItemAction<Task>() {
@Override
public void onEvent(Task choosen, Event event) {
choosen.remove();
}
}).createWithoutSettingContext();
}
return contextMenu;
}
public Planner getPlanner() {
return getGanttPanel().getPlanner();
}
private GanttPanel getGanttPanel() {
return (GanttPanel) getParent();
}
public TaskEditFormComposer getModalFormComposer() {
return taskEditFormComposer;
}
}

View file

@ -0,0 +1,7 @@
package org.zkoss.ganttz;
public interface TaskRemovedListener {
public void taskRemoved(Task taskRemoved);
}

View file

@ -0,0 +1,139 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.zkoss.ganttz;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import org.zkoss.ganttz.util.Interval;
import org.zkoss.ganttz.util.zoom.TimeTrackerState;
import org.zkoss.ganttz.util.zoom.ZoomLevel;
import org.zkoss.ganttz.util.zoom.ZoomLevelChangedListener;
import org.zkoss.ganttz.util.zoom.TimeTrackerState.DetailItem;
import org.zkoss.zk.ui.AbstractComponent;
import org.zkoss.zk.ui.HtmlMacroComponent;
import org.zkoss.zul.Label;
/**
*
*
* @author Francisco Javier Moran Rúa
*
*/
public class TimeTracker extends HtmlMacroComponent {
private static Interval getTestInterval() {
return new Interval(TimeTrackerState.year(2009), TimeTrackerState
.year(2019));
}
private AbstractComponent fakeRow;
private List<WeakReference<ZoomLevelChangedListener>> zoomListeners = new LinkedList<WeakReference<ZoomLevelChangedListener>>();
private DatesMapper datesMapper = null;
private Collection<DetailItem> detailsFirstLevelCached = null;
private Collection<DetailItem> detailsSecondLevelCached = null;
public void addZoomListener(ZoomLevelChangedListener listener) {
zoomListeners
.add(new WeakReference<ZoomLevelChangedListener>(listener));
}
private void fireZoomChanged(ZoomLevel detailLevel) {
ListIterator<WeakReference<ZoomLevelChangedListener>> listIterator = zoomListeners
.listIterator();
while (listIterator.hasNext()) {
ZoomLevelChangedListener listener = listIterator.next().get();
if (listener == null) {
listIterator.remove();
} else {
listener.zoomLevelChanged(detailLevel);
}
}
}
public Collection<TimeTrackerState.DetailItem> getDetailsFirstLevel() {
if (detailsFirstLevelCached == null) {
detailsFirstLevelCached = getTimeTrackerState()
.getFirstLevelDetails(getTestInterval());
}
return detailsFirstLevelCached;
}
public Interval getRealInterval() {
return getTimeTrackerState().getRealIntervalFor(getTestInterval());
}
public Collection<TimeTrackerState.DetailItem> getDetailsSecondLevel()
throws Exception {
if (detailsSecondLevelCached == null) {
detailsSecondLevelCached = getTimeTrackerState()
.getSecondLevelDetails(getTestInterval());
}
return detailsSecondLevelCached;
}
private TimeTrackerState getTimeTrackerState() {
return getDetailLevel().getTimeTrackerState();
}
public int getHorizontalSize() {
// Code to improve. Not optimus. We have to calculate the details twice
int result = 0;
Collection<DetailItem> detailsFirstLevel = getDetailsFirstLevel();
for (TimeTrackerState.DetailItem item : detailsFirstLevel) {
result += item.getSize();
}
return result;
}
public void onIncrease() {
changeDetailLevel(getDetailLevel().next());
}
private void changeDetailLevel(ZoomLevel d) {
setDynamicProperty("detailLevel", d);
datesMapper = null;
detailsFirstLevelCached = null;
detailsSecondLevelCached = null;
final Label lb = (Label) getFellow("mcdetaillevel");
lb.setValue(getDetailLevel().toString());
recreate();
fireZoomChanged(d);
}
public void onDecrease() {
changeDetailLevel(getDetailLevel().previous());
}
private ZoomLevel getDetailLevel() {
return (ZoomLevel) getDynamicProperty("detailLevel");
}
public AbstractComponent getFakeRow() {
return fakeRow;
}
public void setFakeRow(AbstractComponent fakeRow) {
this.fakeRow = fakeRow;
}
public DatesMapper getMapper() {
if (datesMapper == null) {
datesMapper = new DatesMapperOnInterval(getHorizontalSize(),
getRealInterval());
}
return datesMapper;
}
}

View file

@ -0,0 +1,26 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.zkoss.ganttz.util;
/**
*
* @author Francisco Javier Moran Rúa
*
*/
public class GanttUtils {
public static int getIntFromStylePosition(String position) throws Exception {
String[] tokens = position.split("px");
if (tokens.length != 1) {
throw new Exception("Bad formatting for input parameter");
}
return Integer.parseInt(tokens[0]);
}
}

View file

@ -0,0 +1,62 @@
/**
*
*/
package org.zkoss.ganttz.util;
import java.util.Date;
public class Interval {
private final Date start;
private final Date finish;
private final long lengthBetween;
public Interval(Date start, Date finish) {
if (start == null)
throw new IllegalArgumentException("begin cannot be null");
if (finish == null)
throw new IllegalArgumentException("end cannot be null");
if (start.compareTo(finish) > 0)
throw new IllegalArgumentException("start must be prior to end");
this.start = start;
this.finish = finish;
lengthBetween = this.finish.getTime() - this.start.getTime();
}
public Date getStart() {
return new Date(start.getTime());
}
public Date getFinish() {
return new Date(finish.getTime());
}
public long getLengthBetween() {
return lengthBetween;
}
public Date atProportion(double proportion) {
// comparisons with doubles are dangerous, change it
if (proportion > 1.0d) {
throw new IllegalArgumentException(
"the proportion must be less or equal than one");
}
if (proportion < 0d) {
throw new IllegalArgumentException(
"the proportion must be bigger than cero");
}
return new Date(start.getTime() + (int) (lengthBetween * proportion));
}
public double getProportion(Date date) {
if (!isIncluded(date))
throw new IllegalArgumentException("date " + date
+ " must be between [" + start + "," + finish + "]");
return ((double) date.getTime() - start.getTime()) / lengthBetween;
}
private boolean isIncluded(Date date) {
return start.compareTo(date) <= 0 && finish.compareTo(date) >= 0;
}
}

View file

@ -0,0 +1,112 @@
package org.zkoss.ganttz.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.OpenEvent;
import org.zkoss.zul.Menuitem;
import org.zkoss.zul.Menupopup;
import org.zkoss.zul.impl.api.XulElement;
public class MenuBuilder<T extends XulElement> {
public static <T extends XulElement> MenuBuilder<T> on(Page page,
Collection<T> elements) {
return new MenuBuilder<T>(page, elements);
}
public static <T extends XulElement> MenuBuilder<T> on(Page page,
T... elements) {
return on(page, Arrays.asList(elements));
}
public static interface ItemAction<T> {
void onEvent(T choosen, Event event);
}
private class Item {
final String name;
final ItemAction<T> action;
Item(String name, ItemAction<T> action) {
this.name = name;
this.action = action;
}
Menuitem createMenuItem() {
Menuitem result = new Menuitem();
result.setLabel(name);
return result;
}
}
private final List<T> elements;
private final List<Item> items = new ArrayList<Item>();
private Component root;
private MenuBuilder(Page page, Collection<? extends T> elements) {
this.elements = new ArrayList<T>(elements);
this.root = page.getLastRoot();
}
public MenuBuilder<T> item(String name, ItemAction<T> itemAction) {
if (name == null)
throw new IllegalArgumentException("name cannot be null");
if (itemAction == null)
throw new IllegalArgumentException("itemAction cannot be null");
items.add(new Item(name, itemAction));
return this;
}
private T referenced;
public Menupopup createWithoutSettingContext() {
return create(false);
}
public Menupopup create() {
return create(true);
}
private Menupopup create(boolean setContext) {
Menupopup result = new Menupopup();
result.addEventListener("onOpen", new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
OpenEvent openEvent = (OpenEvent) event;
referenced = (T) openEvent.getReference();
}
});
for (final Item item : items) {
Menuitem menuItem = item.createMenuItem();
menuItem.addEventListener("onClick", new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
ItemAction<T> action = item.action;
action.onEvent(referenced, event);
}
});
result.appendChild(menuItem);
}
root.appendChild(result);
if (setContext) {
for (T element : elements) {
element.setContext(result);
}
}
return result;
}
}

View file

@ -0,0 +1,48 @@
package org.zkoss.ganttz.util;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.ListIterator;
public class WeakReferencedListeners<T> {
public interface ListenerNotification<T> {
void doNotify(T listener);
}
public static <T> WeakReferencedListeners<T> create() {
return new WeakReferencedListeners<T>();
}
private LinkedList<WeakReference<T>> listeners = new LinkedList<WeakReference<T>>();
private WeakReferencedListeners() {
}
public synchronized void addListener(T listener) {
if (listener == null)
throw new IllegalArgumentException("listener cannot be null");
listeners.add(new WeakReference<T>(listener));
}
public synchronized void fireEvent(
ListenerNotification<? super T> notification) {
ListIterator<WeakReference<T>> listIterator = listeners.listIterator();
ArrayList<T> active = new ArrayList<T>();
while (listIterator.hasNext()) {
T listener = listIterator.next().get();
if (listener == null)
listIterator.remove();
else {
active.add(listener);
}
}
for (T listener : active) {
notification.doNotify(listener);
}
}
}

View file

@ -0,0 +1,82 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.zkoss.ganttz.util.zoom;
import java.util.Collection;
import java.util.Vector;
import org.zkoss.ganttz.util.Interval;
/**
*
* @author Francisco Javier Moran Rúa
*
*/
public class DetailOneTimeTrackerState extends TimeTrackerState {
public static final DetailOneTimeTrackerState INSTANCE = new DetailOneTimeTrackerState();
private static final int FIRST_LEVEL_ITEM_SIZE = 200;
private static final int SECOND_LEVEL_ITEM_SIZE = 100;
private DetailOneTimeTrackerState() {
};
private Collection<DetailItem> buildCollectionDetailsFirstLevel(
int initialYear, int endYear) {
Collection<DetailItem> detailsVector = new Vector<DetailItem>();
for (int i = initialYear; i <= endYear; i++) {
DetailItem d = new DetailItem(FIRST_LEVEL_ITEM_SIZE, String
.valueOf(i));
detailsVector.add(d);
}
return detailsVector;
}
private Collection<DetailItem> buildCollectionDetailsSecondLevel(
int initialYear, int endYear) {
Collection<DetailItem> detailsVector = new Vector<DetailItem>();
for (int i = initialYear; i <= endYear; i++) {
DetailItem d1 = new DetailItem(SECOND_LEVEL_ITEM_SIZE, "H1");
detailsVector.add(d1);
DetailItem d2 = new DetailItem(SECOND_LEVEL_ITEM_SIZE, "H2");
detailsVector.add(d2);
}
return detailsVector;
}
@Override
protected Collection<DetailItem> createDetailsForFirstLevel(
Interval interval) {
int[] pairYears = calculateInitialEndYear(interval.getStart(), interval
.getFinish());
return buildCollectionDetailsFirstLevel(pairYears[0], pairYears[1]);
}
@Override
protected Collection<DetailItem> createDetailsForSecondLevel(
Interval interval) {
int[] pairYears = calculateInitialEndYear(interval.getStart(), interval
.getFinish());
return buildCollectionDetailsSecondLevel(pairYears[0], pairYears[1]);
}
public Interval getRealIntervalFor(Interval interval) {
int[] pairYears = calculateInitialEndYear(interval.getStart(), interval
.getFinish());
return new Interval(year(pairYears[0]), year(pairYears[1] + 1));
}
}

View file

@ -0,0 +1,146 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.zkoss.ganttz.util.zoom;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Vector;
import org.zkoss.ganttz.util.Interval;
/**
*
* @author Francisco Javier Moran Rúa
*
*/
public class DetailTwoTimeTrackerState extends TimeTrackerState {
public static final DetailTwoTimeTrackerState INSTANCE = new DetailTwoTimeTrackerState();
private static final int FIRST_LEVEL_ITEM_SIZE = 400;
private static final int SECOND_LEVEL_ITEM_SIZE = 100;
public Interval getRealIntervalFor(Interval interval) {
int[] pairYears = calculateInitialEndYear(interval.getStart(), interval
.getFinish());
int startQuarter = calculateInQuarterPeriodDateInYear(interval
.getStart(), pairYears[0]);
int endQuarter = calculateInQuarterPeriodDateInYear(interval
.getFinish(), pairYears[1]);
return new Interval(quarterAt(startQuarter - 1, year(pairYears[0])),
quarterAt(endQuarter, year(pairYears[1])));
}
private static Date quarterAt(int quarter, Date date) {
int year = from(date).get(Calendar.YEAR);
Calendar calendar = from(year(year));
calendar.add(Calendar.MONTH, 3 * quarter);
return calendar.getTime();
}
static Calendar from(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return calendar;
}
private Collection<DetailItem> buildCollectionDetailsFirstLevel(
Date initialDate, Date endDate, int initialYear, int endYear) {
Collection<DetailItem> detailsVector = new Vector<DetailItem>();
// Calculate the size of the first detail of the first level
int quarter = calculateInQuarterPeriodDateInYear(initialDate,
initialYear);
detailsVector.add(new DetailItem((4 - (quarter - 1))
* FIRST_LEVEL_ITEM_SIZE / 4, String.valueOf(initialYear)));
for (int i = (initialYear + 1); i < endYear; i++) {
DetailItem d = new DetailItem(FIRST_LEVEL_ITEM_SIZE, String
.valueOf(i));
detailsVector.add(d);
}
// Calculate the size of the last detail of the first level
int endQuarter = calculateInQuarterPeriodDateInYear(endDate, endYear);
detailsVector
.add(new DetailItem(endQuarter * FIRST_LEVEL_ITEM_SIZE / 4,
String.valueOf(endYear)));
return detailsVector;
}
private Collection<DetailItem> buildCollectionDetailsSecondLevel(
Date initialDate, Date endDate, int initialYear, int endYear) {
ArrayList<DetailItem> result = new ArrayList<DetailItem>();
int startDateQuarter = calculateInQuarterPeriodDateInYear(initialDate,
initialYear);
ArrayList<DetailItem> quarters = new ArrayList<DetailItem>();
for (int i = 0; i < 4; i++) {
quarters.add(new DetailItem(SECOND_LEVEL_ITEM_SIZE, "Q" + (i + 1)));
}
// DetailItem is an inmutable class so it can be safely shared
result.addAll(quarters.subList(startDateQuarter - 1, 4));
for (int i = (initialYear + 1); i < endYear; i++) {
result.addAll(quarters);
}
int quarterEndDate = calculateInQuarterPeriodDateInYear(endDate,
endYear);
result.addAll(quarters.subList(0, quarterEndDate));
return result;
}
/**
*
* @param date
* @param year
* @return a number from 1(quarter until to 1st April) to 4(quarter until
* 1st January of the next year) showing the quarter in which the
* date is for the year
*/
private int calculateInQuarterPeriodDateInYear(Date date, int year) {
Date[] quarters = createQuartersForYear(year);
for (int i = 0; i < quarters.length; i++) {
if (date.before(quarters[i])) {
return i + 1;
}
}
throw new IllegalArgumentException("date " + date + " is not in year "
+ year);
}
private static Date[] createQuartersForYear(int year) {
Date yearDate = year(year);
Date[] result = new Date[4];
for (int i = 0; i < result.length; i++) {
result[i] = quarterAt(i + 1, yearDate);
}
return result;
}
@Override
protected Collection<DetailItem> createDetailsForFirstLevel(
Interval interval) {
int[] pairYears = calculateInitialEndYear(interval.getStart(), interval
.getFinish());
return buildCollectionDetailsFirstLevel(interval.getStart(), interval
.getFinish(), pairYears[0], pairYears[1]);
}
@Override
protected Collection<DetailItem> createDetailsForSecondLevel(
Interval interval) {
int[] pairYears = calculateInitialEndYear(interval.getStart(), interval
.getFinish());
return buildCollectionDetailsSecondLevel(interval.getStart(), interval
.getFinish(), pairYears[0], pairYears[1]);
}
}

View file

@ -0,0 +1,146 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.zkoss.ganttz.util.zoom;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import org.zkoss.ganttz.util.Interval;
/**
*
* @author Francisco Javier Moran Rúa
*
*/
public abstract class TimeTrackerState {
protected static final long MILLSECONDS_IN_DAY = 1000 * 60 * 60 * 24;
protected static final int NUMBER_OF_ITEMS_MINIMUM = 10;
/**
* This class is conceived as an immutable class.
*
* @author Francisco Javier Moran Rúa
*
*/
public final static class DetailItem {
private int size;
private String name;
private final boolean even;
public DetailItem(int size, String name) {
this(size, name, false);
}
public DetailItem(int size, String name, boolean even) {
this.size = size;
this.name = name;
this.even = even;
}
/**
* @return the size
*/
public int getSize() {
return size;
}
/**
* @return the name
*/
public String getName() {
return name;
}
public DetailItem markEven(boolean even) {
return new DetailItem(size, name, even);
}
public boolean isEven() {
return even;
}
}
public Collection<DetailItem> getFirstLevelDetails(Interval interval) {
return markEvens(createDetailsForFirstLevel(interval));
}
private static List<DetailItem> markEvens(
Collection<? extends DetailItem> items) {
boolean even = false;
ArrayList<DetailItem> result = new ArrayList<DetailItem>();
for (DetailItem detailItem : items) {
result.add(detailItem.markEven(even));
even = !even;
}
return result;
}
protected abstract Collection<DetailItem> createDetailsForFirstLevel(
Interval interval);
protected abstract Collection<DetailItem> createDetailsForSecondLevel(
Interval interval);
public Collection<DetailItem> getSecondLevelDetails(Interval interval) {
return markEvens(createDetailsForSecondLevel(interval));
}
protected static int[] calculateInitialEndYear(Date initialDate,
Date endDate) {
int[] pairYears = new int[2];
long yearsInBetween = calculateYearsBetween(initialDate, endDate);
Calendar cal = new GregorianCalendar();
cal.setTime(initialDate);
int initialYear = cal.get(Calendar.YEAR);
int endYear;
if (yearsInBetween >= NUMBER_OF_ITEMS_MINIMUM) {
cal.setTime(endDate);
endYear = cal.get(Calendar.YEAR);
} else {
endYear = initialYear + NUMBER_OF_ITEMS_MINIMUM;
}
pairYears[0] = initialYear;
pairYears[1] = endYear;
return pairYears;
}
protected static long calculateYearsBetween(Date initialDate, Date endDate) {
System.out.println("Initial date:" + initialDate);
System.out.println("End date:" + endDate);
long milsecondsDiff = endDate.getTime() - initialDate.getTime();
// To chech later: If you put MILLSECONDS_IN_YEAR the
// division is made wrongly.
long days = milsecondsDiff / MILLSECONDS_IN_DAY;
return (days / 365);
}
public static Date year(int year) {
Calendar calendar = Calendar.getInstance();
calendar.clear();
calendar.set(Calendar.YEAR, year);
return calendar.getTime();
}
public abstract Interval getRealIntervalFor(Interval testInterval);
}

View file

@ -0,0 +1,58 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.zkoss.ganttz.util.zoom;
/**
*
* @author Francisco Javier Moran Rúa
*
*/
public enum ZoomLevel {
DETAIL_ONE(DetailOneTimeTrackerState.INSTANCE), DETAIL_TWO(
DetailTwoTimeTrackerState.INSTANCE), DETAIL_THREE(
DetailTwoTimeTrackerState.INSTANCE), DETAIL_FOUR(
DetailTwoTimeTrackerState.INSTANCE);
private final TimeTrackerState state;
private ZoomLevel(TimeTrackerState state) {
if (state == null)
throw new IllegalArgumentException("state cannot be null");
this.state = state;
}
/**
*
* @return if there is no next, returns <code>this</code>. Otherwise returns
* the next one.
*/
public ZoomLevel next() {
final int next = ordinal() + 1;
if (next == ZoomLevel.values().length) {
return this;
}
return ZoomLevel.values()[next];
}
/**
*
* @return if there is no previous, returns <code>this</code>. Otherwise
* returns the previous one.
*/
public ZoomLevel previous() {
if (ordinal() == 0) {
return this;
}
return ZoomLevel.values()[ordinal() - 1];
}
public TimeTrackerState getTimeTrackerState() {
return state;
}
}

View file

@ -0,0 +1,8 @@
package org.zkoss.ganttz.util.zoom;
public interface ZoomLevelChangedListener {
public void zoomLevelChanged(ZoomLevel detailLevel);
}

View file

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<language-addon>
<addon-name>ganttz</addon-name>
<language-name>xul/html</language-name>
<extension>zul</extension>
<extension>xul</extension>
<zscript>
import org.zkoss.ganttz.*;
</zscript>
<component>
<component-name>planner</component-name>
<component-class>org.zkoss.ganttz.Planner</component-class>
<mold>
<mold-name>default</mold-name>
<mold-uri>~./ganttz/planner.dsp</mold-uri>
</mold>
</component>
<component>
<component-name>listdetails</component-name>
<component-class>org.zkoss.ganttz.ListDetails</component-class>
<macro-uri>~./ganttz/zul/listdetails.zul</macro-uri>
</component>
<component>
<component-name>taskdetail</component-name>
<component-class>org.zkoss.ganttz.TaskDetail</component-class>
<macro-uri>~./ganttz/zul/taskdetail.zul</macro-uri>
</component>
<component>
<component-name>ganttpanel</component-name>
<component-class>org.zkoss.ganttz.GanttPanel</component-class>
<mold>
<mold-name>default</mold-name>
<mold-uri>~./ganttz/ganttpanel.dsp</mold-uri>
</mold>
</component>
<component>
<component-name>task</component-name>
<component-class>org.zkoss.ganttz.Task</component-class>
<mold>
<mold-name>default</mold-name>
<mold-uri>~./ganttz/task.dsp</mold-uri>
</mold>
</component>
<component>
<component-name>tasklist</component-name>
<component-class>org.zkoss.ganttz.TaskList</component-class>
<mold>
<mold-name>default</mold-name>
<mold-uri>~./ganttz/tasklist.dsp</mold-uri>
</mold>
</component>
<component>
<component-name>dependencylist</component-name>
<component-class>org.zkoss.ganttz.DependencyList</component-class>
<mold>
<mold-name>default</mold-name>
<mold-uri>~./ganttz/dependencylist.dsp</mold-uri>
</mold>
</component>
<component>
<component-name>dependency</component-name>
<component-class>org.zkoss.ganttz.Dependency</component-class>
<mold>
<mold-name>default</mold-name>
<mold-uri>~./ganttz/dependency.dsp</mold-uri>
</mold>
</component>
<component>
<component-name>timetracker</component-name>
<component-class>org.zkoss.ganttz.TimeTracker</component-class>
<macro-uri>~./ganttz/zul/timetracker.zul</macro-uri>
<property>
<property-name>detailLevel</property-name>
<property-value>1</property-value>
</property>
</component>
</language-addon>

View file

@ -0,0 +1,7 @@
<style>
.z-taskassigment-normal {
cursos: move;
border: 2px solid #538BA2;
background-color: #BBBBBB;
}
</style>

View file

@ -0,0 +1,7 @@
<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<%@ taglib uri="http://www.zkoss.org/dsp/zk/core" prefix="z" %>
<c:set var="self" value="${requestScope.arg.self}"/>
<div class="dependency" id="${self.uuid}" z.type="ganttz.dependency.Dependency" idTaskOrig="${self.idTaskOrig}" idTaskEnd="${self.idTaskEnd}" ${self.outerAttrs}">
</div>

View file

@ -0,0 +1,14 @@
<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<%@ taglib uri="http://www.zkoss.org/dsp/zk/core" prefix="z" %>
<c:set var="self" value="${requestScope.arg.self}"/>
<div id="${self.uuid}" z.type="ganttz.dependencylist.Dependencylist" z.autoz="true"${self.outerAttrs}">
<div id="listdependencies">
<c:forEach var="child" items="${self.children}">
${z:redraw(child, null)}
</c:forEach>
</div>
</div>

View file

@ -0,0 +1,16 @@
<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<%@ taglib uri="http://www.zkoss.org/dsp/zk/core" prefix="z" %>
<c:set var="self" value="${requestScope.arg.self}"/>
<div id="ganttpanel">
<div id="${self.uuid}" z.type="ganttz.ganttpanel.GanttPanel" ${self.outerAttrs}>
<c:forEach var="child" items="${self.children}">
${z:redraw(child, null)}
</c:forEach>
</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

View file

@ -0,0 +1,10 @@
<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<%@ taglib uri="http://www.zkoss.org/dsp/zk/core" prefix="z" %>
<c:set var="self" value="${requestScope.arg.self}"/>
<div id="${self.uuid}" z.type="ganttz.planner.Planner" ${self.outerAttrs}">
<c:forEach var="child" items="${self.children}">
${z:redraw(child, null)}
</c:forEach>
</div>

View file

@ -0,0 +1,11 @@
<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<%@ taglib uri="http://www.zkoss.org/dsp/zk/core" prefix="z" %>
<c:set var="self" value="${requestScope.arg.self}"/>
<div id="row${self.uuid}" class="row" z.valor="boxid="${self.uuid}">
<div id="${self.uuid}" z.type="ganttz.task.Task" idTask="${self.id}" z.autoz="true"${self.outerAttrs}">
<span id="${self.uuid}!real">${self.taskName}</span>
</div>
</div>

View file

@ -0,0 +1,43 @@
<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<%@ taglib uri="http://www.zkoss.org/dsp/zk/core" prefix="z" %>
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/yahoo-dom-event/yahoo-dom-event.js" ></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/selector/selector-min.js"></script>
<!-- Drag and Drop source file -->
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/dragdrop/dragdrop-min.js" ></script>
<!-- Combo-handled YUI JS files: -->
<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.7.0/build/yahoo-dom-event/yahoo-dom-event.js&2.7.0/build/dragdrop/dragdrop-min.js"></script>
<!-- Combo-handled YUI CSS files: -->
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/combo?2.7.0/build/resize/assets/skins/sam/resize.css">
<c:include page="~./ganttz/css/task.css.dsp"/>
<!-- Combo-handled YUI JS files: -->
<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.7.0/build/yahoo-dom-event/yahoo-dom-event.js&2.7.0/build/element/element-min.js&2.7.0/build/dragdrop/dragdrop-min.js&2.7.0/build/resize/resize-min.js"></script>
<!-- Source file -->
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/logger/logger-min.js"></script>
<script type="text/javascript">
document.body.class = "yui-skin-sam";
var myContainer = document.body.appendChild(document.createElement("div"));
var myLogReader = new YAHOO.widget.LogReader(myContainer);
</script>
<c:set var="self" value="${requestScope.arg.self}"/>
<div id="${self.uuid}" z.type="ganttz.tasklist.Tasklist" z.autoz="true"${self.outerAttrs}" sameHeightElementId="${self.sameHeightElementId}">
<div id="listtasks">
<c:forEach var="child" items="${self.children}">
${z:redraw(child, null)}
</c:forEach>
</div>
</div>

View file

@ -0,0 +1,29 @@
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c"?>
<zk>
<vbox class="listdetails" id="insertionPoint">
<button label="${c:l('listdetails.add_task')}"
onClick="insertionPoint.getParent().addTask();" />
<taskdetail taskId="task1" start="01/06/2009" length="365 days"
taskName="Tarefa1"
color="blue"/>
<taskdetail taskId="task2" start="01/01/2011" length="365 days"
taskName="Tarefa2"
color="red"/>
<taskdetail taskId="task3" start="01/01/2013" length="365 days"
taskName="Tarefa3"
color="yellow"/>
<taskdetail taskId="task4" start="01/01/2013" length="365 days"
taskName="Tarefa4"
color="yellow"/>
<!--taskdetail taskId="task5" start="01/01/2010" length="100 days"
taskName="Tarefa5"
color="orange"/>
<taskdetail taskId="task6" start="01/01/2010" length="100 days"
taskName="Tarefa6"
color="white"/-->
</vbox>
</zk>

View file

@ -0,0 +1,18 @@
<zk>
<zscript><![CDATA[
top = self;
]]>
</zscript>
<hbox>
<textbox id="nameBox" value="" onChange="top.updateBean();" />
<datebox id="startDateBox" compact="true" onChange="top.updateBean();"/>
<datebox id="endDateBox" compact="true" onChange="top.updateBean();" />
</hbox>
<zscript><![CDATA[
top.nameBox = nameBox;
top.startDateBox = startDateBox;
top.endDateBox = endDateBox;
]]>
</zscript>
</zk>

View file

@ -0,0 +1,53 @@
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c"?>
<zk>
<zscript><![CDATA[
top = self;
]]>
</zscript>
<style dynamic="true">
tr.z-vbox-sep {
height: 0px;
padding: 0px;
margin: 0px;
}
</style>
<vbox>
<hbox>
<button image="zkau/web/ganttz/img/zoom_in.gif" forward="onIncrease"/>
<button image="zkau/web/ganttz/img/zoom_out.gif" forward="onDecrease"/>
<label value="Detail level:"/>
<label id="mcdetaillevel" value="${c:l(arg.detailLevel)}"/>
</hbox>
<div>
<grid>
<columns>
<column label="${each.name}" width="${each.size}px" forEach="${top.detailsFirstLevel}"></column>
</columns>
</grid>
<grid style="width: ${top.horizontalSize+2}px;">
<columns>
<column label="${each.name}" class="timetracker_column${(each.even?'_even':'')}" width="${each.size}px" forEach="${top.detailsSecondLevel}"></column>
</columns>
<rows>
<row id="fake_row" style="height: 100px;">
<div class="fake_column timetracker_column${(each.even?'_even':'')}" style="height: 100px;" forEach="${top.detailsSecondLevel}" />
</row>
</rows>
</grid>
</div>
<zscript><![CDATA[
top.fakeRow = fake_row;
]]>
</zscript>
</vbox>
</zk>

View file

@ -0,0 +1,39 @@
zkDependency = {};
zkDependency.origin = function(dependency) {
return document.getElementById(dependency.getAttribute("idTaskOrig"));
}
zkDependency.destination = function(dependency) {
return document.getElementById(dependency.getAttribute("idTaskEnd"));
}
zkDependency.draw = function(dependency) {
var orig = zkPlanner.findPos(this.origin(dependency));
var dest = zkPlanner.findPos(this.destination(dependency));
// This corner case may depend on dependence type
orig[0] = orig[0] + Math.max( 0,
this.origin(dependency).offsetWidth - zkTask.CORNER_WIDTH);
orig[1] = orig[1] + zkTask.HEIGHT;
dest[1] = dest[1] + zkTask.HALF_HEIGHT;
zkPlanner.drawArrow(dependency, orig, dest);
}
zkDependency.init = function(dependency) {
zkPlanner.setupArrow(dependency);
YAHOO.util.Event.onDOMReady(function() {
zkDependency.draw(dependency);
});
YAHOO.util.Event.onDOMReady(function() {
boxes = [ zkTask.getDD(zkDependency.origin(dependency)), zkTask.getDD(zkDependency.destination(dependency))];
for(var i = 0; i < boxes.length; i++){
boxes[i].on('dragEvent',function(ev){
zkDependency.draw(dependency);
}, null, false);
}
});
}

View file

@ -0,0 +1,4 @@
zkDependencylist = {};
zkDependencylist.init = function (cmp) {
}

View file

@ -0,0 +1,5 @@
zkGanttPanel = {};
zkGanttPanel.init = function(cmp){
}

View file

@ -0,0 +1,101 @@
zkPlanner = {};
zkPlanner.init = function(planner){
}
zkPlanner.findImageElement = function(arrow, name) {
var children = arrow.getElementsByTagName("img");
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child.getAttribute("class").indexOf(name) != -1) {
return child;
}
}
return null;
}
function get_origin() {
return YAHOO.util.Dom.getXY('listdependencies');
}
zkPlanner.findPos = function(obj) {
var pos1 = get_origin();
var pos2 = YAHOO.util.Dom.getXY(obj.id);
return [ pos2[0] - pos1[0], pos2[1] - pos1[1] ];
}
zkPlanner.findPosForMouseCoordinates = function(x, y){
/* var pos1 = get_origin() */
var pos1 = YAHOO.util.Dom.getXY('listtasks');
return [x - pos1[0], y - pos1[1]];
}
zkPlanner.setupArrow = function(arrowDiv){
var base_url_images = "zkau/web/ganttz/img/";
var image_data = [ [ "start", "pixel.gif" ], [ "mid", "pixel.gif" ],
[ "end", "pixel.gif" ], [ "arrow", "arrow.png" ] ];
for ( var i = 0; i < image_data.length; i++) {
var img = document.createElement('img');
img.setAttribute("class", image_data[i][0]+" extra_padding");
img.src = base_url_images + image_data[i][1];
arrowDiv.appendChild(img);
}
}
zkPlanner.drawArrow = function(arrow, orig, dest){
var xorig = orig[0] - zkTask.DEPENDENCY_PADDING;
var yorig = orig[1] - zkTask.HALF_DEPENDENCY_PADDING;
var xend = dest[0] - zkTask.DEPENDENCY_PADDING;
var yend = dest[1] - zkTask.HALF_DEPENDENCY_PADDING;
var width = (xend - xorig);
var xmid = xorig + width;
var depstart = this.findImageElement(arrow, 'start');
/* depstart.style.top = yorig + "px";
depstart.style.left = xorig + "px";
depstart.style.width = width + "px"; */
depstart.style.display = "none";
var depmid = this.findImageElement(arrow, 'mid');
if (yend > yorig) {
depmid.style.top = yorig + "px";
depmid.style.height = yend - yorig + "px";
} else {
depmid.style.top = yend + "px";
depmid.style.height = yorig - yend + "px";
}
depmid.style.left = xorig + "px";
var depend = this.findImageElement(arrow, 'end');
depend.style.top = yend + "px";
depend.style.left = xorig + "px";
depend.style.width = width + "px";
if (width < 0) {
depend.style.left = xend + "px";
depend.style.width = Math.abs(width) + "px";
}
var deparrow = this.findImageElement(arrow, 'arrow');
var baseURL = 'zkau/web/ganttz/img/';
if ( width == 0 ) {
deparrow.src = baseURL+"arrow2.png";
deparrow.style.top = yend - 10 + "px";
deparrow.style.left = xend - 5 + "px";
if ( yorig > yend ) {
deparrow.src = baseURL+"arrow4.png";
deparrow.style.top = yend + "px";
}
} else {
deparrow.style.top = yend - 5 + "px";
deparrow.style.left = xend - 10 + "px";
deparrow.src = baseURL+"arrow.png";
if (width < 0) {
deparrow.src = baseURL+"arrow3.png";
deparrow.style.left = xend + "px";
deparrow.style.top = yend - 5 + "px";
}
}
}

View file

@ -0,0 +1,250 @@
/**
*
* ganttz.js
*
*/
zkTask = {};
zkTask.CORNER_WIDTH = 20;
zkTask.HEIGHT = 20;
zkTask.HALF_HEIGHT = 10;
zkTask.DEPENDENCY_PADDING = 4;
zkTask.HALF_DEPENDENCY_PADDING = 2;
zkTask.getDD = function(cmp) {
if (typeof (cmp.created_dd) == 'undefined') {
// Create the laned drag&drop component
cmp.created_dd = new YAHOO.example.DDRegion(cmp.id, '', {
cont : 'row' + cmp.id
});
}
return cmp.created_dd;
}
zkTask.init = function(cmp) {
// Configure the drag&drop over the component
var dd = zkTask.getDD(cmp);
// Register the event endDragEvent
dd.on('endDragEvent', function(ev) {
zkau.send( {
uuid : cmp.id,
cmd : "updatePosition",
data : [ cmp.style.left, cmp.style.top ]
});
}, null, false);
// Configure the task element to be resizable
var resize = new YAHOO.util.Resize(cmp.id, {
handles : [ 'r' ],
proxy : true
});
resize.on('resize', function(ev) {
zkau.send( {
uuid : cmp.id,
cmd : "updateSize",
data : [ cmp.style.width ]
});
}, zkTask, true);
// Listen to mousemove events
YAHOO.util.Event.on(document.body, 'mousemove', function(e) {
var arrPos = YAHOO.util.Event.getXY(e);
zkTask.xMouse = arrPos[0];
zkTask.yMouse = arrPos[1];
});
};
zkTask.xMouse;
zkTask.yMouse;
zkTask.setAttr = function(cmp, nm, val) {
switch (nm) {
case "style.top":
case "style.width":
case "style.left": {
zkau.setAttr(cmp, nm, val);
return true;
}
}
};
zkTask.addDependency = function(cmp) {
zkTask.createArrow(cmp);
};
zkTask.setAttr = function(cmp, name, value) {
if ("name" == name) {
var span = YAHOO.util.Selector.query("span", cmp, true);
span.innerHTML = value;
return true;
}
return false;
}
zkTask.createArrow = function(cmp) {
var arrow = document.createElement('div');
var listtasksNode = document.getElementById("listtasks");
var listdependenciesNode = document.getElementById("listdependencies");
var cmpNode = document.getElementById(cmp.id);
cmp.parentNode.appendChild(arrow);
zkPlanner.setupArrow(arrow);
var xMouse = zkTask.xMouse;
var yMouse = zkTask.yMouse;
function updateArrow() {
var origin = zkPlanner.findPos(cmp);
origin[0] = origin[0]
+ Math.max(0, cmpNode.offsetWidth - zkTask.CORNER_WIDTH);
origin[1] = origin[1] - listtasksNode.offsetTop
+ listdependenciesNode.offsetTop + zkTask.HEIGHT;
var destination = zkPlanner.findPosForMouseCoordinates(xMouse, yMouse);
zkPlanner.drawArrow(arrow, origin, destination);
}
updateArrow();
mousemoveListener = function(e) {
var arrPos = YAHOO.util.Event.getXY(e);
xMouse = arrPos[0];
yMouse = arrPos[1];
updateArrow();
};
mouseClickListener = function(e) {
var parentNode = arrow.parentNode;
var task;
if (task = zkTask.isOverTask(cmp, arrow)) {
zkau.send( {
uuid : cmp.id,
cmd : "addDependency",
data : [ task.getAttribute('idtask') ]
});
}
parentNode.removeChild(arrow);
YAHOO.util.Event.removeListener(document.body, 'click',
mouseClickListener);
YAHOO.util.Event.removeListener(document.body, 'mousemove',
mousemoveListener);
};
YAHOO.util.Event.on(document.body, 'mousemove', mousemoveListener);
YAHOO.util.Event.on(document.body, 'click', mouseClickListener);
};
zkTask.isOverTask = function(cmp, arrow) {
var listtasksNode = document.getElementById("listtasks");
var ganttPanelNode = document.getElementById("ganttpanel");
arrayTasks = zkTask.getElementsByAttribute(listtasksNode, "div", "z.type",
"ganttz.task.Task");
/* Cursor relative positions to #listtasks (439,160) and ganttPanel scroll */
var xpos = zkTask.xMouse - listtasksNode.offsetLeft
+ ganttPanelNode.scrollLeft;
var ypos = zkTask.yMouse - listtasksNode.offsetTop;
/*
* This way of getting cursor coordinates, is unable to calculate scrollbar
* offset.
*/
for ( var i = 0; i < arrayTasks.length; i++) {
var task = arrayTasks[i];
YAHOO.log(" zkTask.xMouse (" + zkTask.xMouse + "," + zkTask.yMouse
+ ") " + "task.pos (" + xpos + "," + ypos + ") "
+ "task.offsetLeft (" + task.offsetLeft + "," + task.offsetTop
+ ") " + "task.idTask-" + task.toString());
if (((xpos) > (task.offsetLeft))
&& ((xpos) < (task.offsetLeft + task.offsetWidth))
&& (ypos > (task.offsetTop))
&& (ypos < (task.offsetTop + task.offsetHeight))) {
return task;
}
}
return false;
};
zkTask.getElementsByAttribute = function(oElm, strTagName, strAttributeName,
strAttributeValue) {
var arrElements = (strTagName == "*" && oElm.all) ? oElm.all : oElm
.getElementsByTagName(strTagName);
var arrReturnElements = new Array();
var oAttributeValue = (typeof strAttributeValue != "undefined") ? new RegExp(
"(^|\\s)" + strAttributeValue + "(\\s|$)")
: null;
var oCurrent;
var oAttribute;
for ( var i = 0; i < arrElements.length; i++) {
oCurrent = arrElements[i];
oAttribute = oCurrent.getAttribute
&& oCurrent.getAttribute(strAttributeName);
if (typeof oAttribute == "string" && oAttribute.length > 0) {
if (typeof strAttributeValue == "undefined"
|| (oAttributeValue && oAttributeValue.test(oAttribute))) {
arrReturnElements.push(oCurrent);
}
}
}
return arrReturnElements;
}
YAHOO.example.DDRegion = function(id, sGroup, config) {
this.cont = config.cont;
YAHOO.example.DDRegion.superclass.constructor.apply(this, arguments);
};
var myDom = YAHOO.util.Dom, myEvent = YAHOO.util.Event
YAHOO.extend(YAHOO.example.DDRegion, YAHOO.util.DD, {
cont : null,
init : function() {
//Call the parent's init method
YAHOO.example.DDRegion.superclass.init.apply(this, arguments);
this.initConstraints();
myEvent.on(window, 'resize', function() {
this.initConstraints();
}, this, true);
},
initConstraints : function() {
//Get the top, right, bottom and left positions
var region = myDom.getRegion(this.cont);
// Get the element we are working on
var el = this.getEl();
// Get the xy position of it
var xy = myDom.getXY(el);
// Get the width and height
var width = parseInt(myDom.getStyle(el, 'width'), 10);
var height = parseInt(myDom.getStyle(el, 'height'), 10);
// Set left to x minus left
var left = xy[0] - region.left;
// Set right to right minus x minus width
var right = region.right - xy[0] - width;
// Set top to y minus top
var top = xy[1] - region.top;
// Set bottom to bottom minus y minus height
var bottom = region.bottom - xy[1] - height;
// Set the constraints based on the above calculations
this.setXConstraint(left, right);
this.setYConstraint(top, bottom);
}
});

View file

@ -0,0 +1,5 @@
zkTaskDetails = {};
zkTaskDetails.init = function(cmp) {
}

View file

@ -0,0 +1,32 @@
zkTasklist = {};
zkTasklist.init = function(cmp) {
zkTasklist.adjust_height(cmp);
}
zkTasklist.adjust_height = function(cmp) {
var height = cmp.style.height;
var component_to_adjust = document.getElementById(cmp
.getAttribute('sameHeightElementId'));
function setHeight(element, offset) {
if (!offset) {
offset = 0;
}
var newheigth = parseInt(height) + offset;
element.style["height"] = newheigth + 'px';
}
setHeight(document.getElementById('ganttpanel'), 120);/*
* timetracker
* height
*/
if (component_to_adjust) {
setHeight(component_to_adjust);
}
var found = YAHOO.util.Selector.query(".fake_column", component_to_adjust,
false);
found.each( function(element) {
setHeight(element);
});
}

View file

@ -0,0 +1,55 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.navalplanner</groupId>
<artifactId>navalplanner</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>navalplanner-webapp</artifactId>
<packaging>war</packaging>
<name>Naval Planner Web Client Module</name>
<build>
<finalName>navalplanner-webapp</finalName>
</build>
<dependencies>
<!-- ZK -->
<dependency>
<groupId>org.zkoss.zk</groupId>
<artifactId>zul</artifactId>
</dependency>
<dependency>
<groupId>org.zkoss.zk</groupId>
<artifactId>zkplus</artifactId>
</dependency>
<dependency>
<groupId>org.zkoss.zk</groupId>
<artifactId>zk</artifactId>
</dependency>
<!-- Naval Planner ZK Components -->
<dependency>
<groupId>org.navalplanner</groupId>
<artifactId>navalplanner-gantt-zk</artifactId>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</dependency>
<!-- BeanShell (required by ZK)-->
<dependency>
<groupId>bsh</groupId>
<artifactId>bsh</artifactId>
</dependency>
<!-- Apache Commons Fileupload (required by ZK) -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,62 @@
package org.zk.myhello.pages;
import org.zkoss.ganttz.util.zoom.ZoomLevel;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.util.Initiator;
import org.zkoss.zul.Div;
import org.zkoss.zul.Label;
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* @author Francisco Javier Moran Rúa
*
*/
public class MyHelloPageListener extends Div implements Initiator {
private ZoomLevel currentDetailLevel;
public MyHelloPageListener() {
setCurrentDetailLevel(ZoomLevel.DETAIL_ONE);
}
public void doInit(Page arg0, Object[] arg1) throws Exception {
setCurrentDetailLevel(ZoomLevel.DETAIL_ONE);
}
public void doAfterCompose(Page arg0) throws Exception {
setCurrentDetailLevel(ZoomLevel.DETAIL_ONE);
}
public boolean doCatch(Throwable arg0) throws Exception {
return true;
}
public void doFinally() throws Exception {
}
/**
* @return the currentDetailLevel
*/
public ZoomLevel getCurrentDetailLevel() {
return this.currentDetailLevel;
}
/**
* @param currentDetailLevel
* the currentDetailLevel to set
*/
public void setCurrentDetailLevel(ZoomLevel currentDetailLevel) {
this.currentDetailLevel = currentDetailLevel;
}
public void repaint() {
Label l = (Label) getFellow("valor");
l.invalidate();
}
}

View file

@ -0,0 +1,11 @@
task.name=Nome
task.start=Comezo
task.end=Final
task.notes=Notas
task.ok=Aceptar
listdetails.add_task=Engadir
task.new_task_name=Nova Tarefa
DETAIL_ONE=Detalle 1
DETAIL_TWO=Detalle 2
DETAIL_THREE=Detalle 3
DETAIL_FOUR=Detalle 4

View file

@ -0,0 +1,11 @@
task.name=Name
task.start=Start
task.end=End
task.notes=Notes
task.ok=Ok
listdetails.add_task=Add
task.new_task_name=New Task
DETAIL_ONE=Detail 1
DETAIL_TWO=Detail 2
DETAIL_THREE=Detail 3
DETAIL_FOUR=Detail 4

View file

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>
helloworld</display-name>
<!-- /// -->
<!-- DSP -->
<servlet>
<description><![CDATA[The servlet loads the DSP pages.]]></description>
<servlet-name>dspLoader</servlet-name>
<servlet-class>org.zkoss.web.servlet.dsp.InterpreterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dspLoader</servlet-name>
<url-pattern>*.dsp</url-pattern>
</servlet-mapping>
<!-- /// -->
<!-- //// -->
<!-- ZK -->
<listener>
<description>Used to cleanup when a session is destroyed</description>
<display-name>ZK Session Cleaner</display-name>
<listener-class>org.zkoss.zk.ui.http.HttpSessionListener</listener-class>
</listener>
<servlet>
<description>ZK loader for ZUML pages</description>
<servlet-name>zkLoader</servlet-name>
<servlet-class>org.zkoss.zk.ui.http.DHtmlLayoutServlet</servlet-class>
<!-- Must. Specifies URI of the update engine (DHtmlUpdateServlet).
It must be the same as <url-pattern> for the update engine.
-->
<init-param>
<param-name>update-uri</param-name>
<param-value>/zkau</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>zkLoader</servlet-name>
<url-pattern>*.zul</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>zkLoader</servlet-name>
<url-pattern>*.zhtml</url-pattern>
</servlet-mapping>
<servlet>
<description>The asynchronous update engine for ZK</description>
<servlet-name>auEngine</servlet-name>
<servlet-class>org.zkoss.zk.au.http.DHtmlUpdateServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>auEngine</servlet-name>
<url-pattern>/zkau/*</url-pattern>
</servlet-mapping>
<!-- //// -->
<welcome-file-list>
<welcome-file>myhello.zul</welcome-file>
</welcome-file-list>
</web-app>

View file

@ -0,0 +1,5 @@
<zk>
<log>
<log-base></log-base>
</log>
</zk>

View file

@ -0,0 +1,137 @@
/* ProductionManagement Global Styles */
/*** Tasks ***/
#listtasks {
position:relative;
width:400px; /** Constants */
top:100px;
}
.listdetails {
width:200px;
float:left;
margin-top:80px;
}
.listdetails table {
height:30px;
}
.listdetails td {
padding-left:3px;
}
.listdetails input {
width: 90px;
}
.z-datebox-inp {
width:100px;
}
/* Task box properties */
.yui-resize {
border: 1px solid;
text-align:center;
vertical-align: middle;
font-size:0.8em;
z-index:10;
}
/* Task lane properties */
.row {
height: 19px;
border-bottom: dotted 1px #CCCCCC;
margin-bottom: 10px;
margin-top: 10px;
width: 1800px;
}
/*** Dependencies ***/
#listdependencies {
position:relative;
width:400px;
float:left;
top:0px; /* Should be -60, recalc. redrawing deps. */
}
.dependence {
z-index:1;
position: absolute;
}
.end, .start, .mid, .arrow {
position:absolute;
padding:4px;
}
.end, .start {
height:1px;
}
.mid {
width:1px;
}
/*.center {
float:left;
overflow-x:scroll;
width:600px;
}
.footer {
clear:both;
} */
/* Width: ganttpanel constant,
Height: is recalculated on number of tasks */
#ganttpanel {
/* border: 1px solid; */
height:400px;
width: 900px;
overflow-x: scroll;
}
#ganttpanel table {
float:left;
padding:0;
margin:0;
overflow:hidden;
}
#ganttpanel table td {
padding:0;
}
.fake_column.timetracker_column_even {
background-color: #EEEEEE;
}
.timetracker_fake_row {
height: 80px;
}
/*
.extra_padding {
padding: 6px;
} */
.logo {
background-image: url("../img/v3/blue_ga.jpg");
height:50px;
width:300px;
height:100px;
float:left;
clear:both;
position:absolute;
}
.vision {
}
.headings {
height:20px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View file

@ -0,0 +1,63 @@
<?init class="org.zk.myhello.pages.MyHelloPageListener"?>
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c"?>
<?link rel="stylesheet" type="text/css" href="/css/productionmanagement.css"?>
<?page id="myhello"?>
<zk>
<div class="headings">
<div class="logo"></div>
<div class="vision"></div>
</div>
<planner>
<div id="idContextMenuTaskAssigment" use="org.zk.myhello.pages.MyHelloPageListener">
</div>
<listdetails>
</listdetails>
<ganttpanel>
<timetracker detailLevel="${idContextMenuTaskAssigment.currentDetailLevel}" />
<tasklist id="taskList">
<task id="task1" color="blue"/>
<task id="task2" color="red"/>
<task id="task3" color="yellow"/>
<task id="task4" color="green"/>
<!--task id="task5" color="orange"/>
<task id="task6" color="white"/-->
</tasklist>
<window border="normal" width="300px" apply="${taskList.modalFormComposer}">
<grid>
<rows>
<row>${c:l('task.name')} <textbox id="name"/></row>
<row>${c:l('task.start')} <datebox id="startDateBox" compact="true"/>
</row>
<row>
${c:l('task.end')} <datebox id="endDateBox" compact="true" />
</row>
<row>
${c:l('task.notes')} <textbox id="notes" />
</row>
</rows>
</grid>
<button id="ok" label=" ${c:l('task.ok')}" />
</window>
<dependencylist>
<dependency idTaskOrig="task1" idTaskEnd="task2"/>
<dependency idTaskOrig="task2" idTaskEnd="task3"/>
<dependency idTaskOrig="task1" idTaskEnd="task4"/>
</dependencylist>
</ganttpanel>
</planner>
</zk>

338
pom.xml Normal file
View file

@ -0,0 +1,338 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.navalplanner</groupId>
<artifactId>navalplanner</artifactId>
<packaging>pom</packaging>
<version>1.0.0</version>
<name>Naval Planner</name>
<!-- =================================================================== -->
<!-- Modules -->
<modules>
<module>navalplanner-business</module>
<module>navalplanner-gantt-zk</module>
<module>navalplanner-webapp</module>
</modules>
<!-- =================================================================== -->
<!-- Default values for properties. These default values are expected to be
valid for most profiles. In any case, profiles can overwrite values
when necessary.
-->
<properties>
<!-- Data source properties -->
<dataSource.user>naval</dataSource.user>
<dataSource.password>naval</dataSource.password>
<dataSource.jndiName>jdbc/navalplanner-ds</dataSource.jndiName>
<testDataSource.user>${dataSource.user}</testDataSource.user>
<testDataSource.password>${dataSource.password}</testDataSource.password>
</properties>
<!-- =================================================================== -->
<!-- Profiles.
* The build is always executed by selecting at least two non-exclusive
profiles. By default, such profiles are "dev" and "mysql" (meaning
"use MySQL assuming a development environment").
* General profiles. There are two general (database-independent)
profiles: "dev" and "prod". The former is used for development
(including testing) and the latter is used for production (including
testing). As shown below, two dataSources (databases schemas) are
used in both profiles: one for running (dataSource) and another one
for the Maven test fase (testDataSource). Note the Maven test fase
is executed both with development and production profiles.
* Database-specific profiles. There is a profile for each supported
database.
* Specific profiles can be defined to better adapt to a particular
environment by overwriting/adding properties and/or including other
chunks of valid XML.
* Usage:
+ mvn <<goal>> => Execute <<goal>> with default profiles.
+ mvn -Pdev,<<database>> <<goal> => Execute <<goal>> with
development and <<database>> profiles.
+ mvn -Pprod,<<database>> <<goal>> => Execute <<goal>> with
production and <<database>> profiles.
+ Note that when using -P option all desired profiles must be
specified (e.g. "-Pprod" with the intention to select "prod"
and the default database profiles is not correct;
"-Pprod,<<database>>" must be used instead).
* Examples:
+ mvn <<goal>>
+ mvn -Pdev,mysql <<goal>>
+ mvn -Pprod,mysql <<goal>>
-->
<profiles>
<!-- Development profile -->
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<!-- Data source properties -->
<dataSource.url>jdbc:mysql://localhost/navaldev</dataSource.url>
<testDataSource.url>${dataSource.url}test</testDataSource.url>
<!-- Hibernate properties -->
<hibernate.show_sql>true</hibernate.show_sql>
<hibernate.format_sql>true</hibernate.format_sql>
<hibernate.use_sql_comments>true</hibernate.use_sql_comments>
<hibernate.hbm2ddl.auto>update</hibernate.hbm2ddl.auto>
</properties>
</profile>
<!-- Production profile -->
<profile>
<id>prod</id>
<properties>
<!-- Data source properties -->
<!-- NOTE: particular production profiles will probably wish to
overwrite "dataSource.url" property and also maybe
"testDataSource.url".-->
<dataSource.url>jdbc:mysql://localhost/navalprod</dataSource.url>
<testDataSource.url>${dataSource.url}test</testDataSource.url>
<!-- Hibernate properties -->
<hibernate.show_sql>false</hibernate.show_sql>
<hibernate.format_sql>false</hibernate.format_sql>
<hibernate.use_sql_comments>false</hibernate.use_sql_comments>
<hibernate.hbm2ddl.auto>update</hibernate.hbm2ddl.auto>
</properties>
</profile>
<!-- MySQL profile -->
<profile>
<id>mysql</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<!-- JDBC driver properties -->
<jdbcDriver.groupId>mysql</jdbcDriver.groupId>
<jdbcDriver.artifactId>mysql-connector-java</jdbcDriver.artifactId>
<jdbcDriver.version>5.0.5</jdbcDriver.version>
<jdbcDriver.className>com.mysql.jdbc.Driver</jdbcDriver.className>
<!-- Hibernate properties -->
<hibernate.dialect>org.hibernate.dialect.MySQLDialect</hibernate.dialect>
</properties>
</profile>
<profile>
<id>postgresql</id>
<properties>
<!-- JDBC driver properties -->
<jdbcDriver.groupId>postgresql</jdbcDriver.groupId>
<jdbcDriver.artifactId>postgresql</jdbcDriver.artifactId>
<jdbcDriver.version>8.3-603.jdbc4</jdbcDriver.version>
<jdbcDriver.className>org.postgresql.Driver</jdbcDriver.className>
<!-- Data source properties -->
<!-- FIXME: Workaround for current bug in profile-based
approach. -->
<dataSource.url>jdbc:postgresql://localhost/navaldev</dataSource.url>
<!-- Hibernate properties -->
<hibernate.dialect>org.hibernate.dialect.PostgreSQLDialect</hibernate.dialect>
</properties>
</profile>
<!-- Add here other profiles. -->
</profiles>
<!-- =================================================================== -->
<!-- Dependency management -->
<dependencyManagement>
<dependencies>
<!-- JDBC driver -->
<dependency>
<groupId>${jdbcDriver.groupId}</groupId>
<artifactId>${jdbcDriver.artifactId}</artifactId>
<version>${jdbcDriver.version}</version>
<scope>test</scope>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.6.ga</version>
</dependency>
<!-- JUnit -->
<!-- IMPORTANT: Spring TestContext 2.5.x is not compatible with
JUnit 4.5. -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>2.5.6</version>
<scope>test</scope>
</dependency>
<!-- ZK -->
<dependency>
<groupId>org.zkoss.zk</groupId>
<artifactId>zul</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.zkoss.zk</groupId>
<artifactId>zkplus</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.zkoss.zk</groupId>
<artifactId>zk</artifactId>
<version>3.5.2</version>
</dependency>
<!-- Commons Logging (required by many frameworks)-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.0.4</version>
</dependency>
<!-- Naval Planner ZK Components -->
<dependency>
<groupId>org.navalplanner</groupId>
<artifactId>navalplanner-gantt-zk</artifactId>
<version>1.0.0</version>
</dependency>
<!-- BeanShell (required by ZK)-->
<dependency>
<groupId>bsh</groupId>
<artifactId>bsh</artifactId>
<version>2.0b4</version>
<scope>runtime</scope>
</dependency>
<!-- Apache Commons Fileupload (required by ZK) -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.1</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<!-- =============================================================== -->
<!-- Filtering -->
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>../src/main/jetty</directory>
<includes>
<include>jetty-env.xml</include>
</includes>
<targetPath>../jetty</targetPath>
<filtering>true</filtering>
</resource>
</resources>
<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources>
<plugins>
<!-- =========================================================== -->
<!-- Compiler configuration -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<verbose>true</verbose>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- =========================================================== -->
<!-- Assembly configuration -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/assembly/src.xml</descriptor>
</descriptors>
</configuration>
</plugin>
<!-- =========================================================== -->
<!-- Jetty configuration -->
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<!-- FIXME: try to not to specify version. -->
<version>6.1.12.rc2</version>
<configuration>
<jettyEnvXml>target/jetty/jetty-env.xml</jettyEnvXml>
<scanIntervalSeconds>5</scanIntervalSeconds>
<scanTargetPatterns>
<scanTargetPattern>
<directory>src/main/webapp/WEB-INF</directory>
<includes>
<include>*</include>
</includes>
</scanTargetPattern>
</scanTargetPatterns>
<!-- Log to the console. -->
<requestLog implementation="org.mortbay.jetty.NCSARequestLog">
<!-- This do anything for Jetty, but is a workaround
for a Maven bug that prevents the requestLog from
being set. -->
<append>true</append>
</requestLog>
<!--
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>9090</port>
</connector>
</connectors>
-->
</configuration>
<dependencies>
<dependency>
<groupId>${jdbcDriver.groupId}</groupId>
<artifactId>${jdbcDriver.artifactId}</artifactId>
<version>${jdbcDriver.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

21
src/main/assembly/src.xml Normal file
View file

@ -0,0 +1,21 @@
<assembly>
<id>src</id>
<formats>
<format>zip</format>
<format>tar.gz</format>
<format>tar.bz2</format>
</formats>
<fileSets>
<fileSet>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>**/.project</exclude>
<exclude>**/.classpath</exclude>
<exclude>**/.settings/**</exclude>
<exclude>**/target/**</exclude>
</excludes>
</fileSet>
</fileSets>
</assembly>

View file

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN"
"http://jetty.mortbay.org/configure.dtd">
<Configure class="org.mortbay.jetty.webapp.WebAppContext">
<New id="navalplanner-ds" class="org.mortbay.jetty.plus.naming.Resource">
<Arg>${dataSource.jndiName}</Arg>
<Arg>
<New class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<Set name="driverClassName">${jdbcDriver.className}</Set>
<Set name="url">${dataSource.url}</Set>
<Set name="username">${dataSource.user}</Set>
<Set name="password">${dataSource.password}</Set>
</New>
</Arg>
</New>
</Configure>