public interface DAO<EntityType extends Entity> {

	/**
	 * Should an event be fired?
	 */
	public enum FireUpdateEvent { YES, NO }

	/**
	 * Persist the indicated entity to database, don't fire an update event
	 * 
	 * @param entity
	 * @return the primary key
	 */
	EntityType insert(EntityType entity);

	/**
	 * Persist the indicated entity to database, fire an update event when 
	 * {@link FireUpdateEvent.YES} is passed
	 * 
	 * @param entity
	 * @param fireUpdateEvent
	 * @return the primary key
	 */
	EntityType insert(EntityType entity, FireUpdateEvent fireUpdateEvent);
	
	/**
	 * Persist the indicated entity {@link Collection} to database, don't fire an update event
	 * 
	 * @param entity
	 * @return the primary key
	 */
	Collection<EntityType> insert(Collection<EntityType> entities);
	
	/**
	 * Persist the indicated entity {@link Collection} to database, fire an update event when 
	 * {@link FireUpdateEvent.YES} is passed
	 * 
	 * @param entity
	 * @param fireUpdateEvent
	 * @return the primary key
	 */
	Collection<EntityType> insert(Collection<EntityType> entities, FireUpdateEvent fireUpdateEvent);

	/**
	 * Update indicated entity to database, don't fire an update event
	 * 
	 * @param entity
	 */
	void update(EntityType entity);

	/**
	 * Update indicated entity to database, fire an update event when 
	 * {@link FireUpdateEvent.YES} is passed
	 * 
	 * @param entity
	 * @param fireUpdateEvent
	 */
	void update(EntityType entity, FireUpdateEvent fireUpdateEvent);

	/**
	 * Update indicated entity {@link Collection} to database, don't fire an update event
	 * 
	 * @param entity
	 */
	void update(Collection<EntityType> entities);
	
	/**
	 * Update indicated entity {@link Collection} to database, fire an update event when 
	 * {@link FireUpdateEvent.YES} is passed
	 * 
	 * @param entity
	 * @param fireUpdateEvent
	 */
	void update(Collection<EntityType> entities, FireUpdateEvent fireUpdateEvent);
	
	/**
	 * Delete indicated entity from database, don't fire an update event
	 * 
	 * @param entity
	 */
	void delete(EntityType entity);

	/**
	 * Delete indicated entity from database, fire an update event when 
	 * {@link FireUpdateEvent.YES} is passed
	 * 
	 * @param entity
	 * @param fireUpdateEvent
	 */
	void delete(EntityType entity, FireUpdateEvent fireUpdateEvent);

	/**
	 * Delete indicated entity {@link Collection} from database, don't fire an update event
	 * 
	 * @param entity
	 */
	void delete(Collection<EntityType> entities);
	
	/**
	 * Delete indicated entity {@link Collection} from database, fire an update event when 
	 * {@link FireUpdateEvent.YES} is passed
	 * 
	 * @param entity
	 * @param fireUpdateEvent
	 */
	void delete(Collection<EntityType> entities, FireUpdateEvent fireUpdateEvent);
	
	/**
	 * Return the entity class
	 * 
	 * @return entity
	 */
	Class<EntityType> getEntityClass();

	/**
	 * Get the entity manager
	 * 
	 * @return entity
	 */
	EntityManager getEntityManager();

	/**
	 * Retrieve an object using indicated ID
	 * 
	 * @param id
	 * @return entity
	 */
	EntityType find(final Object id);

	/**
	 * Retrieve all entities of the type indicated by the {@link DAO}
	 * 
	 * @return {@link Collection} of entities
	 */
	List<EntityType> findAll();

	/**
	 * Retrieve all entities of the type indicated by the {@link DAO} with the
	 * given {@link Criteria}
	 * 
	 * @param criteriaArray
	 *            - {@link Criterion}s, separated by commas
	 * @return {@link Collection} of entities
	 */
	List<EntityType> findAllWithCriteria(Criterion... criteriaArray);

	/**
	 * Retrieve all entities of the type indicated by the {@link DAO} with the
	 * given {@link Criteria} in the given {@link Order}
	 * 
	 * @param order
	 * 			  - {@link Order}
	 * @param criteriaArray
	 *            - {@link Criterion}s, separated by commas
	 * @return {@link Collection} of entities
	 */
	List<EntityType> findAllWithCriteria(Order order, Criterion... criteriaArray);
	
	/**
	 * Retrieve <b>a single entity</b> of the type indicated by the {@link DAO}
	 * with the given {@link Criteria}. Only use this method when you are sure
	 * there is only one entity retrieved from the database - this just frees
	 * you from the hassle of getting the first and only element out of a
	 * {@link Collection}
	 * 
	 * @param criteriaArray
	 *            - {@link Criterion}s, separated by commas
	 * @return <b>a single entity</b> if the amount of entities
	 *         found in the database is greater than 0, otherwise <i>null</i>
	 */
	EntityType findSingleWithCriteria(Criterion... criteriaArray);
	
	/**
	 * Retrieve <b>a single entity</b> of the type indicated by the {@link DAO}
	 * with the given {@link Criteria} in the given {@link Order}. 
	 * Only use this method when you are sure
	 * there is only one entity retrieved from the database - this just frees
	 * you from the hassle of getting the first and only element out of a
	 * {@link Collection}
	 * 
	 * @param order
	 * 			  - {@link Order}
	 * @param criteriaArray
	 *            - {@link Criterion}s, separated by commas
	 * @return <b>a single entity</b> if the amount of entities
	 *         found in the database is greater than 0, otherwise <i>null</i>
	 */
	EntityType findSingleWithCriteria(Order order, Criterion... criteriaArray);
	
	
	/**
	 * Fire an update event
	 */
	void fireUpdateEvent();
} public class DAOImpl<EntityType extends Entity> implements DAO<EntityType> {

	private Provider<EntityManager> emProvider;
	private EventBus eventBus;
	
	private Class<EntityType> entityClass;

	@SuppressWarnings("unchecked")
	@Inject
	public DAOImpl(TypeLiteral<EntityType> entityType, Provider<EntityManager> emProvider, EventBus eventBus) {
		this.emProvider = emProvider;
		this.eventBus = eventBus;
		entityClass = (Class<EntityType>) entityType.getRawType();
	}
	
	@Override
	@Transactional
	public EntityType insert(EntityType entity) {
		return insert(entity, FireUpdateEvent.NO);
	}
	
	@Override
	@Transactional
	public EntityType insert(EntityType entity, FireUpdateEvent fireUpdateEvent) {
		getEntityManager().clear();
		EntityType mergedEntity = getEntityManager().merge(entity);
		if(fireUpdateEvent == FireUpdateEvent.YES) {
			fireUpdateEvent();
		}
		
		return mergedEntity;
	}
	
	@Override
	@Transactional
	public Collection<EntityType> insert(Collection<EntityType> entities) {
		return insert(entities, FireUpdateEvent.NO);
	}

	@Override
	@Transactional
	public Collection<EntityType> insert(Collection<EntityType> entities, FireUpdateEvent fireUpdateEvent) {
		getEntityManager().clear();
		Collection<EntityType> mergedEntities = new ArrayList<>();
		for (EntityType entity : entities) {
			mergedEntities.add(getEntityManager().merge(entity));
		}
		if(fireUpdateEvent == FireUpdateEvent.YES) {
			fireUpdateEvent();
		}
		
		return mergedEntities;
	}

	@Override
	public EntityType find(final Object id) {
		getEntityManager().clear();
		return (EntityType) getEntityManager().find(getEntityClass(),
				id);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<EntityType> findAllWithCriteria(
			final Criterion... criteriaArray) {
		getEntityManager().clear();
		Session session = (Session) getEntityManager().getDelegate();
		Criteria criteria = session.createCriteria(getEntityClass());
		for (Criterion criterion : criteriaArray) {
			criteria.add(criterion);
		}
		return criteria.list();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<EntityType> findAllWithCriteria(Order order,
			Criterion... criteriaArray) {
		getEntityManager().clear();
		Session session = (Session) getEntityManager().getDelegate();
		Criteria criteria = session.createCriteria(getEntityClass());
		criteria.addOrder(order);
		for(Criterion criterion : criteriaArray) {
			criteria.add(criterion);
		}
		
		return criteria.list();
	}

	@Override
	public List<EntityType> findAll() {
		getEntityManager().clear();
		return getEntityManager().createQuery(
				"from " + getEntityClass().getSimpleName(), getEntityClass())
				.getResultList();
	}

	@Override
	public EntityType findSingleWithCriteria(Criterion... criteriaArray) {
		getEntityManager().clear();
		List<EntityType> resultList = findAllWithCriteria(criteriaArray);
		if (resultList.size() > 0) {
			return resultList.get(0);
		} else {
			return null;
		}
	}

	@Override
	public EntityType findSingleWithCriteria(Order order,
			Criterion... criteriaArray) {
		getEntityManager().clear();
		List<EntityType> resultList = findAllWithCriteria(order, criteriaArray);
		if(resultList.size() > 0) {
			return resultList.get(0);
		} else {
			return null;
		}
	}

	@Override
	@Transactional
	public void update(EntityType entity) {
		update(entity, FireUpdateEvent.NO);
	}

	@Override
	@Transactional
	public void update(EntityType entity, FireUpdateEvent fireUpdateEvent) {
		getEntityManager().clear();
		getEntityManager().merge(entity);
		if(fireUpdateEvent == FireUpdateEvent.YES) {
			fireUpdateEvent();
		}
	}

	@Override
	@Transactional
	public void update(Collection<EntityType> entities) {
		update(entities, FireUpdateEvent.NO);
	}

	@Override
	@Transactional
	public void update(Collection<EntityType> entities, FireUpdateEvent fireUpdateEvent) {
		getEntityManager().clear();
		for (EntityType entity : entities) {
			getEntityManager().merge(entity);
		}
		if(fireUpdateEvent == FireUpdateEvent.YES) {
			fireUpdateEvent();
		}
	}

	@Override
	@Transactional
	public void delete(EntityType entity) {
		delete(entity, FireUpdateEvent.NO);
	}

	@Override
	@Transactional
	public void delete(EntityType entity, FireUpdateEvent fireUpdateEvent) {
		getEntityManager().clear();
		getEntityManager().remove(getEntityManager().merge(entity));
		if(fireUpdateEvent == FireUpdateEvent.YES) {
			fireUpdateEvent();
		}
	}
	
	@Override
	@Transactional
	public void delete(Collection<EntityType> entities) {
		delete(entities, FireUpdateEvent.NO);
	}

	@Override
	@Transactional
	public void delete(Collection<EntityType> entities, FireUpdateEvent fireUpdateEvent) {
		getEntityManager().clear();
		for (EntityType entity : entities) {
			getEntityManager().remove(getEntityManager().merge(entity));
		}
		if(fireUpdateEvent == FireUpdateEvent.YES) {
			fireUpdateEvent();
		}
	}

	@Override
	public EntityManager getEntityManager() {
		return emProvider.get();
	}
	
	@Override
	public Class<EntityType> getEntityClass() {
		return entityClass;
	}

	@Override
	public void fireUpdateEvent() {
		eventBus.post(new EntityUpdateEvent(getEntityClass()));
	}
} @Entity
@Table(name="borrowedMaterial")
public class BorrowedMaterial implements de.dhbw.humbuch.model.entity.Entity, Serializable, Comparable<BorrowedMaterial> {
	private static final long serialVersionUID = -7956138735111492455L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	
	@ManyToOne(fetch=FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
	@JoinColumn(name="studentId", referencedColumnName="id")
	private Student student;
	
	@ManyToOne(fetch=FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
	@JoinColumn(name="teachingMaterialId", referencedColumnName="id")
	private TeachingMaterial teachingMaterial;
	
	private Date borrowFrom;
	private Date borrowUntil;
	private Date returnDate;
	private boolean received;
	
	/**
	 * Required by Hibernate.<p>
	 * Use the {@link Builder} instead.
	 * 
	 * @see Builder
	 */
	@Deprecated
	public BorrowedMaterial() {}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public Student getStudent() {
		return student;
	}

	public void setStudent(Student student) {
		this.student = student;
	}

	public TeachingMaterial getTeachingMaterial() {
		return teachingMaterial;
	}

	public void setTeachingMaterial(TeachingMaterial teachingMaterial) {
		this.teachingMaterial = teachingMaterial;
	}

	public Date getBorrowFrom() {
		return borrowFrom;
	}

	public void setBorrowFrom(Date borrowFrom) {
		this.borrowFrom = borrowFrom;
	}

	public Date getBorrowUntil() {
		return borrowUntil;
	}

	public void setBorrowUntil(Date borrowUntil) {
		this.borrowUntil = borrowUntil;
	}

	public Date getReturnDate() {
		return returnDate;
	}

	public void setReturnDate(Date returnDate) {
		this.returnDate = returnDate;
	}

	public boolean isReceived() {
		return received;
	}
	
	public boolean isReturned() {
		return getReturnDate() != null ? true : false;
	}
	
	public void setReceived(boolean received) {
		this.received = received;
	}

	public static class Builder {
		private final Student student;
		private final TeachingMaterial teachingMaterial;
		private final Date borrowFrom;
		
		private Date borrowUntil;
		private Date returnDate;
		private boolean received;
		
		public Builder(Student student, TeachingMaterial teachingMaterial, Date borrowFrom) {
			this.student = student;
			this.teachingMaterial = teachingMaterial;
			this.borrowFrom = borrowFrom;
		}
		
		public Builder borrowUntil(Date borrowUntil) {
			this.borrowUntil = borrowUntil;
			return this;
		}
		
		public Builder returnDate(Date returnDate) {
			this.returnDate = returnDate;
			return this;
		}
		
		public Builder received(boolean received) {
			this.received = received;
			return this;
		}
		
		public BorrowedMaterial build() {
			return new BorrowedMaterial(this);
		}
	}
	
	private BorrowedMaterial(Builder builder) {
		this.student = builder.student;
		this.teachingMaterial = builder.teachingMaterial;
		this.borrowFrom = builder.borrowFrom;
		
		this.borrowUntil = builder.borrowUntil;
		this.returnDate = builder.returnDate;
		this.received = builder.received;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof BorrowedMaterial))
			return false;
		BorrowedMaterial other = (BorrowedMaterial) obj;
		if (getId() != other.getId())
			return false;
		return true;
	}

	@Override
	public int compareTo(BorrowedMaterial o) {
		int compareResult = getTeachingMaterial().compareTo(o.getTeachingMaterial());
		if(compareResult != 0) {
			return compareResult;
		}
		
		return Integer.compare(hashCode(), o.hashCode());
	}
	
} @Entity
@Table(name="category")
public class Category implements de.dhbw.humbuch.model.entity.Entity, Serializable, Comparable<Category> {
	private static final long serialVersionUID = -1497919629033299136L;
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	private String name;
	private String description;
	
	@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy="category", fetch=FetchType.LAZY)
	private List<TeachingMaterial> teachingMaterials = new ArrayList<>();
	
	/**
	 * Required by Hibernate.<p>
	 * Use the {@link Builder} instead.
	 * 
	 * @see Builder
	 */
	@Deprecated
	public Category() {}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public List<TeachingMaterial> getTeachingMaterials() {
		return teachingMaterials;
	}

	public void setTeachingMaterials(List<TeachingMaterial> teachingMaterials) {
		this.teachingMaterials = teachingMaterials;
	}

	public static class Builder {
		private final String name;
		
		private String description;
		
		public Builder(String name) {
			this.name = name;
		}
		
		public Builder description(String description) {
			this.description = description;
			return this;
		}
		
		public Category build() {
			return new Category(this);
		}
	}
	
	private Category(Builder builder) {
		this.name = builder.name;
		
		this.description = builder.description;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Category))
			return false;
		Category other = (Category) obj;
		if (getId() != other.getId())
			return false;
		return true;
	}
	
	@Override
	public String toString() {
		return getName();
	}

	@Override
	public int compareTo(Category o) {
		int compareResult = getName().compareTo(o.getName());
		if(compareResult != 0) {
			return compareResult;
		}
		
		return Integer.compare(hashCode(), o.hashCode());
	}
} @Entity
@Table(name="dunning")
public class Dunning implements de.dhbw.humbuch.model.entity.Entity, Serializable {
	private static final long serialVersionUID = -6187908554803908912L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;

	@ManyToOne(fetch=FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
	@JoinColumn(name="studentId", referencedColumnName="id")
	private Student student;
	
	@Enumerated(EnumType.STRING)
	@Column(name="type")
	private Type type = Type.TYPE1;
	
	@Enumerated(EnumType.STRING)
	@Column(name="status")
	private Status status;
	
	@ManyToMany(fetch = FetchType.LAZY)
	@JoinTable(
			name="dunning_has_borrowedMaterial",
			joinColumns={@JoinColumn(name="dunningId", referencedColumnName="id")},
			inverseJoinColumns={@JoinColumn(name="borrowedMaterialId", referencedColumnName="id"),
								@JoinColumn(name="borrowedMaterial_studentId", referencedColumnName="studentId")}
			)
	private Set<BorrowedMaterial> borrowedMaterials = new HashSet<BorrowedMaterial>();
	
	@ElementCollection
	@MapKeyEnumerated(EnumType.STRING)
	@MapKeyColumn(name="status")
	@CollectionTable(name="dunningDate", joinColumns = @JoinColumn(name="dunningId"))
	@Column(name="statusDate")
	private Map<Status, Date> statusDateMapping = new HashMap<Status, Date>();
	
	/**
	 * Required by Hibernate.<p>
	 * Use the {@link Builder} instead.
	 * 
	 * @see Builder
	 */
	@Deprecated
	public Dunning() {}
	
	public Date getStatusDate(Status status) {
		return statusDateMapping.get(status);
	}
	
	public Status getStatus() {
		return status;
	}

	public void setStatus(Status status) {
		if(this.status != status) {
			this.status = status;
			statusDateMapping.put(status, new Date());
		}
	}

	public Set<BorrowedMaterial> getBorrowedMaterials() {
		return borrowedMaterials;
	}

	public void setBorrowedMaterials(Set<BorrowedMaterial> borrowedMaterials) {
		this.borrowedMaterials = borrowedMaterials;
	}

	public boolean addBorrowedMaterials(BorrowedMaterial borrowedMaterial) {
		return borrowedMaterials.add(borrowedMaterial);
	}
	
	public int getId() {
		return id;
	}

	public Student getStudent() {
		return student;
	}

	public Type getType() {
		return type;
	}

	public static class Builder {
		private int id;
		private Student student;
		private Type type;
		private Status status;
		private Set<BorrowedMaterial> borrowedMaterials;
		
		public Builder(Student student) {
			this.student = student;
		}
		
		public Builder type(Type type) {
			this.type = type;
			return this;
		}
		
		public Builder status(Status status) {
			this.status = status;
			return this;
		}
		
		public Builder borrowedMaterials(Set<BorrowedMaterial> borrowedMaterials) {
			this.borrowedMaterials = borrowedMaterials;
			return this;
		}
		
		public Builder id(int id) {
			this.id = id;
			return this;
		}
		
		public Dunning build() {
			return new Dunning(this);
		}
	}
	
	private Dunning(Builder builder) {
		id = builder.id;
		student = builder.student;
		type = builder.type;
		setStatus(Status.OPENED);
		status = builder.status;
		borrowedMaterials = builder.borrowedMaterials;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Dunning))
			return false;
		Dunning other = (Dunning) obj;
		if (getId() != other.getId())
			return false;
		return true;
	}

	public enum Status {
		OPENED("Offen"),
		SENT("Versendet"),
		CLOSED("Erledigt");

		private String value;
		
		private Status(String value) {
			this.value = value;
		}

		@Override
		public String toString() {
			return value;
		}
	}
	
	public enum Type {
		TYPE1("1. Mahnung"), 
		TYPE2("2. Mahnung");

		private String value;

		private Type(String value) {
			this.value = value;
		}
		
		@Override
		public String toString() {
			return value;
		}
	}
} @Entity
@Table(name="grade")
public class Grade implements de.dhbw.humbuch.model.entity.Entity, Serializable, Comparable<Grade> {
	private static final long serialVersionUID = 227472334995076701L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	
	private int grade;
	
	@Column(name="suffix")
	private String suffix;
	
	private String teacher;
	
	@OneToMany(mappedBy="grade")
	private List<Student> students = new ArrayList<>();

	/**
	 * Required by Hibernate.<p>
	 * Use the {@link Builder} instead.
	 * 
	 * @see Builder
	 */
	@Deprecated
	public Grade() {}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getGrade() {
		return grade;
	}

	public void setGrade(int grade) {
		this.grade = grade;
	}

	public String getSuffix() {
		return suffix;
	}

	public void setSuffix(String suffix) {
		this.suffix = suffix;
	}

	public List<Student> getStudents() {
		return students;
	}

	public void setStudents(List<Student> students) {
		this.students = students;
	}

	public String getTeacher() {
		return teacher;
	}

	public void setTeacher(String teacher) {
		this.teacher = teacher;
	}
	
	public static class Builder {
		private final int grade;
		private final String suffix;
		
		private String teacher;
		
		public Builder(String gradeString){
			String[] splittedString = splitBetweenCharsAndDigits(gradeString);
			this.grade = Integer.parseInt(splittedString[0]);
			this.suffix = splittedString[1];
		}
		
		public Builder(int grade, String suffix) {
			this.grade = grade;
			this.suffix = suffix;
		}
		
		public Builder teacher(String teacher) {
			this.teacher = teacher;
			return this;
		}
		
		public Grade build() {
			return new Grade(this);
		}
		
		private static String[] splitBetweenCharsAndDigits(String str){	
			return str.split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
		}
	}
	
	private Grade(Builder builder) {
		grade = builder.grade;
		suffix = builder.suffix;
		
		teacher = builder.teacher;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Grade))
			return false;
		Grade other = (Grade) obj;
		if (getId() != other.getId())
			return false;
		return true;
	}

	@Override
	public String toString() {
		return grade + suffix;
	}

	@Override
	public int compareTo(Grade o) {
		int compareResult = Integer.compare(getGrade(), o.getGrade());
		if(compareResult != 0) {
			return compareResult;
		}
		
		compareResult = getSuffix().compareTo(o.getSuffix());
		if(compareResult != 0) {
			return compareResult;
		}
		
		return Integer.compare(hashCode(), o.hashCode());
	}
	
} @Entity
@Table(name="parent")
public class Parent implements de.dhbw.humbuch.model.entity.Entity, Serializable {
	private static final long serialVersionUID = -969816596774143705L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	private String title;
	private String firstname;
	private String lastname;
	private String street;
	private int postcode;
	private String city;
	
	@OneToMany(mappedBy="parent", fetch=FetchType.LAZY)
	private List<Student> studentList = new ArrayList<>();
	
	/**
	 * Required by Hibernate.<p>
	 * Use the {@link Builder} instead.
	 * 
	 * @see Builder
	 */
	@Deprecated
	public Parent() {}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getFirstname() {
		return firstname;
	}

	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}

	public String getLastname() {
		return lastname;
	}

	public void setLastname(String lastname) {
		this.lastname = lastname;
	}

	public String getStreet() {
		return street;
	}

	public void setStreet(String street) {
		this.street = street;
	}

	public int getPostcode() {
		return postcode;
	}

	public void setPostcode(int postcode) {
		this.postcode = postcode;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}
	
	public static class Builder {
		private final String firstname;
		private final String lastname;

		private String title;
		private String street;
		private int postcode;
		private String city;
		
		public Builder(String firstname, String lastname) {
			this.firstname = firstname;
			this.lastname = lastname;
		}
		
		public Builder title(String title) {
			this.title = title;
			return this;
		}
		
		public Builder street(String street) {
			this.street = street;
			return this;
		}
		
		public Builder postcode(int postcode) {
			this.postcode = postcode; 
			return this;
		}
		
		public Builder city(String city) {
			this.city = city;
			return this;
		}
		
		public Parent build() {
			return new Parent(this);
		}
	}

	private Parent(Builder builder) {
		firstname = builder.firstname;
		lastname = builder.lastname;

		title = builder.title;
		street = builder.street;
		postcode = builder.postcode;
		city = builder.city;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Parent))
			return false;
		Parent other = (Parent) obj;
		if (getId() != other.getId())
			return false;
		return true;
	}
	
} @Entity
@Table(name="schoolYear")
public class SchoolYear implements de.dhbw.humbuch.model.entity.Entity, Serializable, Comparable<SchoolYear> {
	private static final long serialVersionUID = -3752454317452902743L;

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int id;
	
	private String year;
	private Date fromDate;
	private Date toDate;
	private Date endFirstTerm;
	private Date beginSecondTerm;
	
	/**
	 * Required by Hibernate.<p>
	 * Use the {@link Builder} instead.
	 * 
	 * @see Builder
	 */
	@Deprecated
	public SchoolYear() {}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getYear() {
		return year;
	}

	public void setYear(String year) {
		this.year = year;
	}

	public Date getFromDate() {
		return fromDate;
	}

	public void setFromDate(Date fromDate) {
		this.fromDate = fromDate;
	}

	public Date getToDate() {
		return toDate;
	}

	public void setToDate(Date toDate) {
		this.toDate = toDate;
	}

	public Date getEndFirstTerm() {
		return endFirstTerm;
	}

	public void setEndFirstTerm(Date endFirstTerm) {
		this.endFirstTerm = endFirstTerm;
	}

	public Date getBeginSecondTerm() {
		return beginSecondTerm;
	}

	public void setBeginSecondTerm(Date beginSecondTerm) {
		this.beginSecondTerm = beginSecondTerm;
	}
	
	public Date getEndOf(Term term) {
		return term == Term.FIRST ? getEndFirstTerm() : getToDate();
	}
	
	public Term getRecentlyActiveTerm() {
		return getBeginSecondTerm().after(new Date()) ? Term.FIRST : Term.SECOND;
	}
	
	public boolean isActive() {
		Date today = new Date();
		return getFromDate().before(today) && getToDate().after(today) ? true : false;
	}
	
	public enum Term {
		FIRST("1. Halbjahr"),
		SECOND("2. Halbjahr");
		
		private String value;
		
		private Term(String value) {
			this.value = value;
		}
		
		@Override
		public String toString() {
			return value;
		}
	}
	
	public static class Builder {
		private final String year;
		private final Date fromDate;
		private final Date toDate;
		
		private Date endFirstTerm;
		private Date beginSecondTerm;
		
		public Builder(String year, Date fromDate, Date toDate) {
			this.year = year;
			this.fromDate = fromDate;
			this.toDate = toDate;
		}
		
		public Builder endFirstTerm(Date endFirstTerm) {
			this.endFirstTerm = endFirstTerm;
			return this;
		}
		
		public Builder beginSecondTerm(Date beginSecondTerm) {
			this.beginSecondTerm = beginSecondTerm;
			return this;
		}
		
		public SchoolYear build() {
			return new SchoolYear(this);
		}
	}
	
	private SchoolYear(Builder builder) {
		this.year = builder.year;
		this.fromDate = builder.fromDate;
		this.toDate = builder.toDate;

		this.endFirstTerm = builder.endFirstTerm;
		this.beginSecondTerm = builder.beginSecondTerm;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof SchoolYear))
			return false;
		SchoolYear other = (SchoolYear) obj;
		if (getId() != other.getId())
			return false;
		return true;
	}

	@Override
	public int compareTo(SchoolYear o) {
		int compareResult = getFromDate().compareTo(o.getFromDate());
		if(compareResult != 0) {
			return compareResult;
		}
		
		return Integer.compare(hashCode(), o.hashCode());
	}
	
} @Entity
@Table(name="setting")
public class SettingsEntry implements Map.Entry<String, String>, de.dhbw.humbuch.model.entity.Entity, Serializable  {
	private static final long serialVersionUID = -2998393509203242955L;

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int id;
	
	private String settingKey;
	private String settingValue;
	private String settingStandardValue;

	/**
	 * Required by Hibernate.<p>
	 * Use the {@link Builder} instead.
	 * 
	 * @see Builder
	 */
	@Deprecated
	public SettingsEntry() {}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getSettingKey() {
		return settingKey;
	}

	public void setSettingKey(String settingKey) {
		this.settingKey = settingKey;
	}

	public String getSettingValue() {
		return settingValue;
	}

	public void setSettingValue(String settingValue) {
		this.settingValue = settingValue;
	}
	
	public String getSettingStandardValue() {
		return settingStandardValue;
	}

	public void setSettingStandardValue(String settingStandardValue) {
		this.settingStandardValue = settingStandardValue;
	}

	@Override
	public String getKey() {
		return settingKey;
	}

	@Override
	public String getValue() {
		return settingValue;
	}

	@Override
	public String setValue(String value) {
		return this.settingValue = value;
	}
	
	public static class Builder {
		private String key;
		private String value;
		private String standardValue;
		
		public Builder(String key, String value, String standardValue) {
			this.key = key;
			this.value = value;
			this.standardValue = standardValue;
		}
		
		public SettingsEntry build() {
			return new SettingsEntry(this);
		}
	}
	
	public SettingsEntry(Builder builder) {
		this.settingKey = builder.key;
		this.settingValue = builder.value;
		this.settingStandardValue = builder.standardValue;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((settingKey == null) ? 0 : settingKey.hashCode());
		result = prime * result + ((settingValue == null) ? 0 : settingValue.hashCode());
		result = prime * result + ((settingStandardValue == null) ? 0 : settingStandardValue.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof SettingsEntry))
			return false;
		SettingsEntry other = (SettingsEntry) obj;
		if (settingKey == null) {
			if (other.settingKey != null)
				return false;
		} else if (!settingKey.equals(other.settingKey))
			return false;
		if (settingValue == null) {
			if (other.settingValue != null)
				return false;
		} else if (!settingValue.equals(other.settingValue))
			return false;
		if (settingStandardValue == null) {
			if (other.settingStandardValue != null)
				return false;
		} else if (!settingStandardValue.equals(other.settingStandardValue))
			return false;
		return true;
	}
} @Entity
@Table(name="student")
public class Student implements de.dhbw.humbuch.model.entity.Entity, Serializable, Comparable<Student> {
	private static final long serialVersionUID = -3020872456290703528L;

	@Id
	private int id;
	
	@ManyToOne(fetch=FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
	@JoinColumn(name="gradeId", referencedColumnName="id")
	private Grade grade;
	private String lastname;
	private String firstname;
	private Date birthday;
	private String gender;
	private boolean leavingSchool;
	
	@OneToMany(mappedBy="student", fetch=FetchType.LAZY)
	private List<BorrowedMaterial> borrowedMaterials = new ArrayList<BorrowedMaterial>();

	@ManyToMany(fetch = FetchType.LAZY)
	@JoinTable(
			name="borrowedMaterial",
			joinColumns={@JoinColumn(name="studentId", referencedColumnName="id")},
			inverseJoinColumns={@JoinColumn(name="teachingMaterialId", referencedColumnName="id")})
	private Collection<TeachingMaterial> teachingMaterials = new HashSet<>();
	
	@ElementCollection(targetClass=Subject.class)
	@Enumerated(EnumType.STRING)
	@CollectionTable(name="studentSubject", joinColumns = @JoinColumn(name="studentId"))
	@Column(name="subject")
	private Set<Subject> profile = EnumSet.noneOf(Subject.class);
	
	@ManyToOne(fetch=FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
	@JoinColumn(name="parentId", referencedColumnName="id")
	private Parent parent;
	
	@OneToMany(mappedBy="student", fetch=FetchType.LAZY)
	private List<Dunning> dunnings = new ArrayList<Dunning>();
	
	/**
	 * Required by Hibernate.<p>
	 * Use the {@link Builder} instead.
	 * 
	 * @see Builder
	 */
	@Deprecated
	public Student() {}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public Grade getGrade() {
		return grade;
	}

	public void setGrade(Grade grade) {
		this.grade = grade;
	}

	public String getLastname() {
		return lastname;
	}

	public void setLastname(String lastname) {
		this.lastname = lastname;
	}

	public String getFirstname() {
		return firstname;
	}

	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}

	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	public String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}

	public Parent getParent() {
		return parent;
	}

	public void setParent(Parent parent) {
		this.parent = parent;
	}

	public List<BorrowedMaterial> getBorrowedMaterials() {
		return borrowedMaterials;
	}

	public void setBorrowedMaterials(List<BorrowedMaterial> borrowedMaterials) {
		this.borrowedMaterials = borrowedMaterials;
	}
	
	public Collection<TeachingMaterial> getTeachingMaterials() {
		return teachingMaterials;
	}

	public Set<Subject> getProfile() {
		return profile;
	}

	public void setProfile(Set<Subject> profile) {
		this.profile = profile;
	}
	
	public void addSubject(Subject subject) {
		getProfile().add(subject);
	}
	
	public boolean hasSubject(Subject subject) {
		return getProfile().contains(subject);
	}
	
	public boolean isLeavingSchool() {
		return leavingSchool;
	}

	public void setLeavingSchool(boolean leavingSchool) {
		this.leavingSchool = leavingSchool;
	}
	
	public List<Dunning> getDunnings() {
		return dunnings;
	}

	public List<BorrowedMaterial> getUnreceivedBorrowedList() {
		List<BorrowedMaterial> unreceivedBorrowedMaterials = new ArrayList<BorrowedMaterial>();
		for (BorrowedMaterial borrowedMaterial : getBorrowedMaterials()) {
			if(!borrowedMaterial.isReceived()) {
				unreceivedBorrowedMaterials.add(borrowedMaterial);
			}
		}
		
		return unreceivedBorrowedMaterials;
	}
	
	public List<BorrowedMaterial> getReceivedBorrowedMaterials() {
		List<BorrowedMaterial> receivedBorrowedMaterials = getBorrowedMaterials();
		receivedBorrowedMaterials.removeAll(getUnreceivedBorrowedList());
		
		return receivedBorrowedMaterials;
	}
	
	public List<BorrowedMaterial> getUnreturnedBorrowedMaterials() {
		List<BorrowedMaterial> unreturnedBorrowedMaterials = new ArrayList<BorrowedMaterial>();
		
		for(BorrowedMaterial borrowedMaterial : getReceivedBorrowedMaterials()) {
			if(borrowedMaterial.getReturnDate() == null) {
				unreturnedBorrowedMaterials.add(borrowedMaterial);
			}
		}
		
		return unreturnedBorrowedMaterials;
	}
	
	public boolean hasUnreceivedBorrowedMaterials() {
		for(BorrowedMaterial borrowedMaterial : getBorrowedMaterials()) {
			if(!borrowedMaterial.isReceived()) {
				return true;
			}
		}
		
		return false;
	}
	
	public void copyDataFrom(Student student) {
		setBirthday(student.getBirthday());
		setFirstname(student.getFirstname());
		setGender(student.getGender());
		setGrade(student.getGrade());
		setLastname(student.getLastname());
		setParent(student.getParent());
		setProfile(student.getProfile());
	}

	public static class Builder {
		private final int id;
		private final String firstname;
		private final String lastname;
		private final Date birthday;
		private final Grade grade;
		
		private String gender;
		private List<BorrowedMaterial> borrowedList = new ArrayList<BorrowedMaterial>();
		private Set<Subject> profile = EnumSet.noneOf(Subject.class);
		private Parent parent;
		private boolean leavingSchool;
		
		public Builder(int id, String firstname, String lastname, Date birthday, Grade grade) {
			this.id = id;
			this.firstname = firstname;
			this.lastname = lastname;
			this.birthday = birthday;
			this.grade = grade;
		}
		
		public Builder gender(String gender) {
			this.gender = gender;
			return this;
		}
		
		public Builder borrowedList(List<BorrowedMaterial> borrowedList) {
			this.borrowedList = borrowedList;
			return this;
		}
		
		public Builder profile(Set<Subject> profile) {
			this.profile = profile;
			return this;
		}
		
		public Builder parent(Parent parent) {
			this.parent = parent;
			return this;
		}
		
		public Builder leavingSchool(boolean leavingSchool){
			this.leavingSchool = leavingSchool;
			return this;
		}
		
		public Student build() {
			return new Student(this);
		}
	}
	
	private Student(Builder builder) {
		id = builder.id;
		firstname = builder.firstname;
		lastname = builder.lastname;
		birthday = builder.birthday;
		grade = builder.grade;
		leavingSchool = builder.leavingSchool;
		
		gender = builder.gender;
		borrowedMaterials = builder.borrowedList;
		profile = builder.profile;
		parent = builder.parent;
		
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Student))
			return false;
		Student other = (Student) obj;
		if (getId() != other.getId())
			return false;
		return true;
	}

	@Override
	public int compareTo(Student o) {
		int compareResult = getLastname().compareTo(o.getLastname());
		if(compareResult != 0) {
			return compareResult;
		}
		
		compareResult = getFirstname().compareTo(o.getFirstname());
		if(compareResult != 0) {
			return compareResult;
		}
		
		return Integer.compare(hashCode(), o.hashCode());
	}
} @Entity
@Table(name="teachingMaterial")
public class TeachingMaterial implements de.dhbw.humbuch.model.entity.Entity, Serializable, Comparable<TeachingMaterial> {
	private static final long serialVersionUID = -6153270685462221761L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	
	@ManyToOne(fetch=FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
	@JoinColumn(name="categoryId", referencedColumnName="id")
	private Category category;
	
	@ElementCollection(targetClass=Subject.class)
	@Enumerated(EnumType.STRING)
	@CollectionTable(name="teachingMaterialSubject", joinColumns = @JoinColumn(name="teachingMaterialId"))
	@Column(name="subject")
	private Set<Subject> profile = EnumSet.noneOf(Subject.class);
	
	private String name;
	private String producer;
	private String identifyingNumber;
	private double price;
	private String comment;
	
	private int fromGrade;
	private int toGrade;
	
	@Enumerated(EnumType.ORDINAL)
	private Term fromTerm = Term.FIRST;
	
	@Enumerated(EnumType.ORDINAL)
	private Term toTerm = Term.SECOND;
	
	private Date validFrom;
	private Date validUntil;
	
	/**
	 * Required by Hibernate.<p>
	 * Use the {@link Builder} instead.
	 * 
	 * @see Builder
	 */
	@Deprecated
	public TeachingMaterial() {}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public Category getCategory() {
		return category;
	}

	public void setCategory(Category category) {
		this.category = category;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getProducer() {
		return producer;
	}

	public void setProducer(String producer) {
		this.producer = producer;
	}

	public String getIdentifyingNumber() {
		return identifyingNumber;
	}

	public void setIdentifyingNumber(String identifyingNumber) {
		this.identifyingNumber = identifyingNumber;
	}

	public double getPrice() {
		return price;
	}

	public void setPrice(double price) {
		this.price = price;
	}

	public String getComment() {
		return comment;
	}

	public void setComment(String comment) {
		this.comment = comment;
	}

	public int getFromGrade() {
		return fromGrade;
	}

	public void setFromGrade(int fromGrade) {
		this.fromGrade = fromGrade;
	}

	public int getToGrade() {
		return toGrade;
	}

	public void setToGrade(int toGrade) {
		this.toGrade = toGrade;
	}

	public Term getFromTerm() {
		return fromTerm;
	}

	public void setFromTerm(Term fromTerm) {
		this.fromTerm = fromTerm;
	}

	public Term getToTerm() {
		return toTerm;
	}

	public void setToTerm(Term toTerm) {
		this.toTerm = toTerm;
	}

	public Date getValidFrom() {
		return validFrom;
	}

	public void setValidFrom(Date validFrom) {
		this.validFrom = validFrom;
	}

	public Date getValidUntil() {
		return validUntil;
	}

	public void setValidUntil(Date validUntil) {
		this.validUntil = validUntil;
	}

	public Set<Subject> getProfile() {
		return profile;
	}

	public void setProfile(Set<Subject> profile) {
		this.profile = profile;
	}

	public void addSubject(Subject subject) {
		getProfile().add(subject);
	}
	
	public boolean hasSubject(Subject subject) {
		return getProfile().contains(subject);
	}
	
	public static class Builder {
		private final Category category;
		private final String name;
		private final String identifyingNumber;
		private final Date validFrom;
		
		private Set<Subject> profile;
		private String producer;
		private double price;
		private String comment;
		private int fromGrade;
		private int toGrade;
		private Term fromTerm;
		private Term toTerm;
		private Date validUntil;
		
		public Builder(Category category, String name, String identifyingNumber, Date validFrom) {
			this.category = category;
			this.name = name;
			this.identifyingNumber = identifyingNumber;
			this.validFrom = validFrom;
			this.fromTerm = Term.FIRST;
			this.toTerm = Term.SECOND;
		}
		
		public Builder profile(Set<Subject> profile) {
			this.profile = profile;
			return this;
		}
		
		public Builder producer(String producer) {
			this.producer = producer;
			return this;
		}
		
		public Builder price(double price) {
			this.price = price;
			return this;
		}
		
		public Builder comment(String comment) {
			this.comment = comment;
			return this;
		}
		
		public Builder fromGrade(int fromGrade) {
			this.fromGrade = fromGrade;
			return this;
		}
		
		public Builder toGrade(int toGrade) {
			this.toGrade = toGrade;
			return this;
		}
		
		public Builder fromTerm(Term fromTerm) {
			this.fromTerm = fromTerm;
			return this;
		}
		
		public Builder toTerm(Term toTerm) {
			this.toTerm = toTerm;
			return this;
		}
		
		public Builder validUntil(Date validUntil) {
			this.validUntil = validUntil;
			return this;
		}
		
		public TeachingMaterial build() {
			return new TeachingMaterial(this);
		}
	}
	
	private TeachingMaterial(Builder builder) {
		this.category = builder.category;
		this.name = builder.name;
		this.identifyingNumber = builder.identifyingNumber;
		this.validFrom = builder.validFrom;
		
		this.profile = builder.profile;
		this.producer = builder.producer;
		this.price = builder.price;
		this.comment = builder.comment;
		this.fromGrade = builder.fromGrade;
		this.toGrade = builder.toGrade;
		this.fromTerm = builder.fromTerm;
		this.toTerm = builder.toTerm;
		this.validUntil = builder.validUntil;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof TeachingMaterial))
			return false;
		TeachingMaterial other = (TeachingMaterial) obj;
		if (getId() != other.getId())
			return false;
		return true;
	}

	@Override
	public int compareTo(TeachingMaterial o) {
		int compareResult = getName().compareTo(o.getName());
		if(compareResult != 0) {
			return compareResult;
		}
		
		return Integer.compare(hashCode(), o.hashCode());
	}
	
} @Entity
@Table(name="user")
public class User implements de.dhbw.humbuch.model.entity.Entity, Serializable {
	private static final long serialVersionUID = 12872765838454735L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	private String username;
	private String password;
	private String email;
	
	@ManyToMany
	@JoinTable(
			name="user_has_role",
			joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
		    inverseJoinColumns={@JoinColumn(name="role_id", referencedColumnName="id")}
			)
	private List<Role> roles = new ArrayList<Role>();
	
	/**
	 * Required by Hibernate.<p>
	 * Use the {@link Builder} instead.
	 * 
	 * @see Builder
	 */
	@Deprecated
	public User() {}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public List<Role> getRoles() {
		return roles;
	}

	public void setRoles(List<Role> roles) {
		this.roles = roles;
	}

	public static class Builder {
		private final String username;
		private final String password;
		
		private String email;
		
		public Builder(String username, String password) {
			this.username = username;
			this.password = password;
		}
		
		public Builder email(String email) {
			this.email = email;
			return this;
		}
		
		public User build() {
			return new User(this);
		}
	}
	
	private User(Builder builder) {
		this.username = builder.username;
		this.password = builder.password;
		
		this.email = builder.email;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof User))
			return false;
		User other = (User) obj;
		if (getId() != other.getId())
			return false;
		return true;
	}
} public class MVVMConfig {
	
	private final static Logger LOG = LoggerFactory
			.getLogger(MVVMConfig.class);

	@Inject
	public MVVMConfig(ViewModelComposer viewModelComposer) {
		viewModelComposer.addStateChangeWrapper(AbstractField.class,
				new StateChangeWrapper() {
					@Override
					public StateChangeListener getStateChangeListener(
							final Object notified) {
						return new StateChangeListener() {
							@SuppressWarnings("unchecked")
							@Override
							public void stateChange(Object value) {
								((AbstractField<Object>) notified)
										.setValue(value);
							}
						};
					}
				});

		viewModelComposer.addSourceWrapper(AbstractField.class,
				new SourceWrapper<Object>() {
					@SuppressWarnings("unchecked")
					@Override
					public Object get(Object source) {
						return ((AbstractField<Object>) source).getValue();
					}
				});

		viewModelComposer.addSourceWrapper(State.class,
				new SourceWrapper<Object>() {
					@SuppressWarnings("unchecked")
					@Override
					public Object get(Object source) {
						return ((State<Object>) source).get();
					}
				});

		viewModelComposer.addActionWrapper(ValueChangeNotifier.class,
				new ActionWrapper() {
					@Override
					public void addActionHandler(Object notifier,
							final ActionHandler actionHandler) {
						((ValueChangeNotifier) notifier)
								.addValueChangeListener(new ValueChangeListener() {
									private static final long serialVersionUID = -854400079672018869L;

									@Override
									public void valueChange(
											ValueChangeEvent event) {
										try {
											actionHandler.handle(event
													.getProperty().getValue());
										} catch (UnsupportedOperationException e) {
											LOG.error("valueChange error", e);
										}
									}
								});
					}
				});

		viewModelComposer.addActionWrapper(Button.class, new ActionWrapper() {
			@Override
			public void addActionHandler(Object notifier,
					final ActionHandler actionHandler) {
				((Button) notifier).addClickListener(new ClickListener() {
					private static final long serialVersionUID = 3154305342571215268L;

					@Override
					public void buttonClick(ClickEvent event) {
						actionHandler.handle(event.getButton().getData());
					}
				});
			}
		});

		viewModelComposer.addStateChangeWrapper(State.class,
				new StateChangeWrapper() {
					@Override
					public StateChangeListener getStateChangeListener(
							final Object notified) {
						return new StateChangeListener() {
							@SuppressWarnings("unchecked")
							@Override
							public void stateChange(Object value) {
								((State<Object>) notified).set(value);
							}
						};
					}
				});
	}
}
 @Theme("humbuch")
@SuppressWarnings("serial")
@Widgetset("com.vaadin.DefaultWidgetSet")
@Title("HumBuch Schulbuchverwaltung")
public class MainUI extends ScopedUI {

	private final static Logger LOG = LoggerFactory.getLogger(MainUI.class);

	public static final String LOGIN_VIEW = "login_view";
	public static final String BOOK_MANAGEMENT_VIEW = "book_management_view";
	public static final String DUNNING_VIEW = "dunning_view";
	public static final String LENDING_VIEW = "lending_view";
	public static final String RETURN_VIEW = "return_view";
	public static final String STUDENT_INFORMATION_VIEW = "student_information_view";
	public static final String SETTINGS_VIEW = "settings_view";
	public static final String ERROR_VIEW = "error_view";

	@Inject
	private LoginView loginView;

	@Inject
	private DunningView dunningView;

	@Inject
	private LendingView lendingView;

	@Inject
	private ReturnView returnView;

	@Inject
	private TeachingMaterialView bookManagementView;

	@Inject
	private StudentInformationView studentInformationView;

	@Inject
	private SettingsView settingsView;

	@Inject
	private ErrorView errorView;

	private Header header = new Header();
	private VerticalLayout viewContainer = new VerticalLayout();;
	private GridLayout root;
	private ComponentContainerViewDisplay ccViewDisplay;
	private Sidebar sidebar;
	private Panel panelContent = new Panel();
	private View currentView;

	private LoginViewModel loginViewModel;
	public Navigator navigator;

	@BindState(IsLoggedIn.class)
	private BasicState<Boolean> isLoggedIn = new BasicState<Boolean>(Boolean.class);

	@Inject
	public MainUI(ViewModelComposer viewModelComposer, LoginViewModel loginViewModel, EventBus eventBus, MVVMConfig mvvmConfig) {
		this.loginViewModel = loginViewModel;
		eventBus.register(this);
		bindViewModel(viewModelComposer, loginViewModel);
	}

	@Override
	protected void init(VaadinRequest request) {

		getUI().getSession().setLocale(new Locale("de", "DE"));

		ccViewDisplay = new ComponentContainerViewDisplay(viewContainer);
		navigator = new Navigator(UI.getCurrent(), ccViewDisplay);

		navigator.setErrorView(errorView);

		navigator.addView("", lendingView);
		navigator.addView(LOGIN_VIEW, loginView);
		navigator.addView(BOOK_MANAGEMENT_VIEW, bookManagementView);
		navigator.addView(DUNNING_VIEW, dunningView);
		navigator.addView(LENDING_VIEW, lendingView);
		navigator.addView(RETURN_VIEW, returnView);
		navigator.addView(STUDENT_INFORMATION_VIEW, studentInformationView);
		navigator.addView(SETTINGS_VIEW, settingsView);

		// Make the displayed view as big as possible
		viewContainer.setSizeFull();

		if (!isLoggedIn.get()) {
			buildMainView(true);
		} else {
			buildMainView(false);
		}

		attachListener();

	}

	/**
	 * Build the MainView with header, navigation bar and footer
	 * 
	 * @param cancel
	 *            Whether the MainView should be build or not
	 */
	private void buildMainView(boolean cancel) {
		if (cancel) {
			setContent(viewContainer);
			return;
		}

		root = new GridLayout(2, 2);

		header.setWidth("100%");
		sidebar = new Sidebar();
		sidebar.setWidth("150px");
		sidebar.getLogoutButton().addClickListener(new ClickListener() {
			@Override
			public void buttonClick(ClickEvent event) {
				loginViewModel.doLogout(new Object());
			}
		});

		panelContent.setContent(viewContainer);
		panelContent.setSizeFull();

		root.setSizeFull();
		root.setRowExpandRatio(1, 1);
		root.setColumnExpandRatio(0, 0);
		root.setColumnExpandRatio(1, 1);
		root.addStyleName("main-view");

		root.addComponent(panelContent, 1, 1);
		root.addComponent(header, 1, 0);
		root.addComponent(sidebar, 0, 0, 0, 1);

		setContent(root);
	}

	/**
	 * Attaches all listeners to the components.
	 */
	private void attachListener() {

		/**
		 * Checks if the user is logged in before the view changes
		 */
		navigator.addViewChangeListener(new ViewChangeListener() {

			@Override
			public boolean beforeViewChange(ViewChangeEvent event) {
				currentView = event.getNewView();
				boolean isLoginView = currentView instanceof LoginView;

				if (!isLoggedIn.get() && !isLoginView) {
					// Redirect to login view always if a user has not yet
					// logged in
					getNavigator().navigateTo(MainUI.LOGIN_VIEW);
					return false;

				} else if (isLoggedIn.get() && isLoginView) {
					// If someone tries to access to login view while logged in,
					// then cancel
					return false;
				}

				return true;
			}

			@Override
			public void afterViewChange(ViewChangeEvent event) {
				if (isLoggedIn.get()) {
					View newView = event.getNewView();
					if (newView instanceof ViewInformation) {
						ViewInformation view = (ViewInformation) newView;
						panelContent.setCaption(((ViewInformation) view)
								.getTitle());
						sidebar.changeMenuBarSelection(event.getViewName());
					} else {
						LOG.warn("New View does not implement ViewInformation interface."
								+ " Could not set caption of panel correctly.");
					}
				}
			}

		});

		/**
		 * Listens for a login or logout of a user an constructs the UI
		 * accordingly
		 */
		isLoggedIn.addStateChangeListener(new StateChangeListener() {

			@Override
			public void stateChange(Object arg0) {
				if (isLoggedIn.get()) {
					buildMainView(false);
				} else {
					buildMainView(true);
					getNavigator().navigateTo(MainUI.LOGIN_VIEW);
				}

			}

		});

		/**
		 * Adds the help HTML to the help button.
		 */
		header.getHelpButton().addClickListener(new ClickListener() {
			@Override
			public void buttonClick(ClickEvent event) {
				String name = "help/" + currentView.getClass().getSimpleName() + ".html";
				ResourceLoader res = new ResourceLoader(name);
				StreamResource sr = new StreamResource(res, "Hilfe");
				new PrintingComponent(sr, "Hilfe", MIMEType.HTML);
			}
		});
	}

	/**
	 * Handles {@link MessageEvent}s showing the message in a Vaadin
	 * {@link Notification}
	 * 
	 * @param messageEvent
	 *            {@link MessageEvent} containing the message to show
	 */
	@Subscribe
	public void handleMessageEvent(MessageEvent messageEvent) {
		Type notificationType;
		switch (messageEvent.type) {
		case ERROR:
			notificationType = Type.ERROR_MESSAGE;
			break;
		case WARNING:
			notificationType = Type.WARNING_MESSAGE;
			break;
		case TRAYINFO:
			notificationType = Type.TRAY_NOTIFICATION;
			break;
		case INFO:
		default:
			notificationType = Type.HUMANIZED_MESSAGE;
		}

		Notification.show(messageEvent.caption, messageEvent.message, notificationType);
	}

	/**
	 * Handles {@link ConfirmEvent}s showing a window with a cancel and a
	 * confirm button.
	 * 
	 * @param confirmEvent
	 *            {@link ConfirmEvent}s containing the confirmable information
	 */
	@Subscribe
	public void handleConfirmEvent(final ConfirmEvent confirmEvent) {
		ConfirmDialog.show(confirmEvent.caption, confirmEvent.message,
				confirmEvent.confirmCaption, confirmEvent.cancelCaption,
				new ConfirmDialog.Listener() {

					@Override
					public void onClose(ConfirmDialog dialog) {
						if (dialog.isConfirmed()) {
							confirmEvent.confirm();
						} else {
							confirmEvent.cancel();
						}
					}
				});
	}

	private void bindViewModel(ViewModelComposer viewModelComposer,
			Object... viewModels) {
		try {
			viewModelComposer.bind(this, viewModels);
		} catch (IllegalAccessException | NoSuchElementException
				| UnsupportedOperationException e) {
			e.printStackTrace();
		}
	}
} public class LoginViewModel {

	private final static Logger LOG = LoggerFactory.getLogger(MainUI.class);
	
	public interface IsLoggedIn extends State<Boolean> {}

	public interface DoLogout extends ActionHandler {}
	public interface DoLogin extends ActionHandler {}
	
	private DAO<User> daoUser;
	private EventBus eventBus;
	private Properties properties;

	@ProvidesState(IsLoggedIn.class)
	public final BasicState<Boolean> isLoggedIn = new BasicState<Boolean>(Boolean.class);
	
	/**
	 * Constructor
	 * 
	 * @param daoUser
	 * @param properties
	 * @param eventBus
	 */
	@Inject
	public LoginViewModel(DAO<User> daoUser, Properties properties, EventBus eventBus) {
		this.properties = properties;
		this.eventBus = eventBus;
		this.daoUser = daoUser;
		updateLoginStatus();
	}
	
	private void updateLoginStatus() {
		isLoggedIn.set(properties.currentUser.get() != null);
	}

	/**
	 * Validates {@code username} and {@code password}. Fires an event if something is wrong.<br>
	 * Sets the logged in {@link User} in the corresponding {@link Properties} state
	 * 
	 * @param username
	 * @param password
	 */
	@HandlesAction(DoLogin.class)
	public void doLogin(String username, String password) {
		properties.currentUser.set(null);
		updateLoginStatus();
		try {
			if (username.equals("") || password.equals("")) {
				eventBus.post(new LoginEvent("Bitte geben Sie einen Nutzernamen und Passwort an."));
				return;
			} else {
				List<User> user = (List<User>) daoUser.findAllWithCriteria(Restrictions.eq("username", username));
				if(!user.isEmpty()) {
						if(PasswordHash.validatePassword(password, user.get(0).getPassword())) {
							properties.currentUser.set(user.get(0));
							updateLoginStatus();
							return;
						}
				}
				eventBus.post(new LoginEvent("Username oder Passwort stimmen nicht überein."));
			}
		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
			eventBus.post(new LoginEvent("Fehler bei Login. Bitte kontaktieren Sie einen Entwickler."));
			LOG.warn(e.getMessage());
			return;
		}
	}

	@HandlesAction(DoLogout.class)
	public void doLogout(Object obj) {
		properties.currentUser.set(null);
		updateLoginStatus();
	}

} public class Properties {
	
	private DAO<SettingsEntry> daoSettingsEnty;
	
	public final State<User> currentUser = new BasicState<>(User.class);
	public final State<Map<String, String>> settings = new BasicState<>(Map.class);
	
	/**
	 * Constructor
	 * 
	 * @param daoSettingsEntry
	 */
	@Inject
	public Properties(DAO<SettingsEntry> daoSettingsEntry) {
		this.daoSettingsEnty = daoSettingsEntry;
		setSettings();
	}
	
	private void setSettings() {
		Map<String, String> settingsMap = new HashMap<>();
		for(SettingsEntry entry : daoSettingsEnty.findAll()) {
			settingsMap.put(entry.getKey(), entry.getValue());
		}
		
		settings.set(settingsMap);
	}
}
import com.google.common.eventbus.EventBus;
import com.google.inject.Module;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.persist.jpa.JpaPersistModule;
import com.google.inject.servlet.ServletModule;
import com.google.inject.servlet.SessionScoped;
import com.vaadin.ui.UI;
import de.davherrmann.guice.vaadin.UIScoped;
import de.davherrmann.mvvm.ViewModelComposer;
import de.dhbw.humbuch.model.DAO;
import de.dhbw.humbuch.model.DAOImpl;
import de.dhbw.humbuch.model.entity.BorrowedMaterial;
import de.dhbw.humbuch.model.entity.Category;
import de.dhbw.humbuch.model.entity.Dunning;
import de.dhbw.humbuch.model.entity.Grade;
import de.dhbw.humbuch.model.entity.Parent;
import de.dhbw.humbuch.model.entity.SchoolYear;
import de.dhbw.humbuch.model.entity.SettingsEntry;
import de.dhbw.humbuch.model.entity.Student;
import de.dhbw.humbuch.model.entity.TeachingMaterial;
import de.dhbw.humbuch.model.entity.User;
import de.dhbw.humbuch.view.MVVMConfig;
import de.dhbw.humbuch.view.MainUI;
import de.dhbw.humbuch.viewmodel.LoginViewModel;
import de.dhbw.humbuch.viewmodel.Properties;
package de.dhbw.humbuch.guice;




/**
 * Guice {@link Module} for binding classes to instances/providers in different scopes
 * 
 * @author davherrmann
 */
public class BasicModule extends ServletModule {

	@Override
	protected void configureServlets() {
		install(new JpaPersistModule("persistModule"));

		filter("/*").through(PersistFilter.class);
		serve("/*").with(BasicServlet.class);

		bind(new TypeLiteral<DAO<Student>>() {}).to(new TypeLiteral<DAOImpl<Student>>() {});