public class ConfirmEvent {
	
	public final UUID id;
	
	public final String caption;
	public final String message;
	public final String confirmCaption;
	public final String cancelCaption;
	private final Runnable confirmRunnable;
	private final Runnable cancelRunnable;
	
	private static final String DEFAULT_WINDOW_CAPTION = "Bestätigen";
	private static final String DEFAULT_CONFIRM_CAPTION = "Ok";
	private static final String DEFAULT_CANCEL_CAPTION = "Abbrechen";
	
	public void confirm() {
		if(confirmRunnable != null)
			confirmRunnable.run();
	}

	public void cancel() {
		if(cancelRunnable != null)
			cancelRunnable.run();
	}
	
	public static class Builder {
		private final String message;
		private String caption;
		private String confirmCaption;
		private String cancelCaption;
		private Runnable confirmRunnable;
		private Runnable cancelRunnable;
		
		public Builder(String message) {
			this.message = message;
			this.caption = DEFAULT_WINDOW_CAPTION;
			this.confirmCaption = DEFAULT_CONFIRM_CAPTION;
			this.cancelCaption = DEFAULT_CANCEL_CAPTION;
		}
		
		public Builder caption(String caption) {
			this.caption = caption;
			return this;
		}
		
		public Builder confirmCaption(String confirmCaption) {
			this.confirmCaption = confirmCaption;
			return this;
		}
		
		public Builder cancelCaption(String cancelCaption) {
			this.cancelCaption = cancelCaption;
			return this;
		}
		
		public Builder confirmRunnable(Runnable runnable) {
			this.confirmRunnable = runnable;
			return this;
		}
		
		public Builder cancelRunnable(Runnable runnable) {
			this.cancelRunnable = runnable;
			return this;
		}
		
		public ConfirmEvent build() {
			return new ConfirmEvent(this);
		}
	}
	
	private ConfirmEvent(Builder builder) {
		this.caption = builder.caption;
		this.message = builder.message;
		this.confirmCaption = builder.confirmCaption;
		this.cancelCaption = builder.cancelCaption;
		this.confirmRunnable = builder.confirmRunnable;
		this.cancelRunnable = builder.cancelRunnable;
		
		id = UUID.randomUUID();
	}
}
 public class MessageEvent {

	public final String caption;
	public final String message;
	public final Type type;

	public enum Type {
		INFO, TRAYINFO, WARNING, ERROR;
	}

	/**
	 * Creates an event with the specified caption and {@link Type}.INFO as
	 * standard type
	 * 
	 * @param message
	 *            {@link String} containing the message
	 */
	public MessageEvent(String caption) {
		this(caption, "", Type.INFO);
	}

	/**
	 * Creates an event with the specified caption, message and {@link Type}.INFO as
	 * standard type
	 * 
	 * @param caption
	 * 			  {@link String} containing the caption
	 * @param message
	 *            {@link String} containing the message
	 */
	public MessageEvent(String caption, String message) {
		this(caption, message, Type.INFO);
	}
	
	/**
	 * Creates an event with the specified caption, empty message and type
	 * 
	 * @param caption
	 * 			  {@link String} containing the caption
	 * @param message
	 *            {@link String} containing the message
	 */
	public MessageEvent(String caption, Type type) {
		this(caption, "", type);
	}
	
	/**
	 * Creates an event with the specified caption, message and type
	 * 
	 * @param caption
	 * 			  {@link String} containing the caption
	 * @param message
	 *            {@link String} containing the message
	 * @param type
	 *            {@link Type} defining the message type
	 */
	public MessageEvent(String caption, String message, Type type) {
		this.caption = caption;
		this.message = message;
		this.type = type;
	}
}
 public enum Type {
	INFO, TRAYINFO, WARNING, ERROR;
}
 @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="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="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;
	}
} public class SettingsViewModel {

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

	public interface DoUpdateUser extends ActionHandler {}
	public interface DoPasswordChange extends ActionHandler {}

	public interface SchoolYears extends State<Collection<SchoolYear>> {}
	public interface Categories extends State<Collection<Category>> {}
	public interface SettingsEntries extends State<Collection<SettingsEntry>> {}
	public interface PasswordChangeStatus extends State<ChangeStatus> {}
	public interface UserName extends State<String> {}
	public interface UserEmail extends State<String> {}

	@ProvidesState(SchoolYears.class)
	public final State<Collection<SchoolYear>> schoolYears = new BasicState<>(Collection.class);

	@ProvidesState(Categories.class)
	public final State<Collection<Category>> categories = new BasicState<>(Collection.class);

	@ProvidesState(SettingsEntries.class)
	public final State<Collection<SettingsEntry>> settingsEntries = new BasicState<>(Collection.class);

	@ProvidesState(PasswordChangeStatus.class)
	public final State<ChangeStatus> passwordChangeStatus = new BasicState<>(ChangeStatus.class);

	@ProvidesState(UserName.class)
	public final State<String> userName = new BasicState<>(String.class);

	@ProvidesState(UserEmail.class)
	public final State<String> userEmail = new BasicState<>(String.class);

	private EventBus eventBus;
	private State<User> currentUser;
	private DAO<SchoolYear> daoSchoolYear;
	private DAO<User> daoUser;
	private DAO<Category> daoCategory;
	private DAO<SettingsEntry> daoSettingsEntry;

	/**
	 * Constructor
	 * 
	 * @param daoSchoolYear
	 * @param daoUser
	 * @param daoCategory
	 * @param daoSettingsEntry
	 * @param properties
	 * @param eventBus
	 */
	@Inject
	public SettingsViewModel(DAO<SchoolYear> daoSchoolYear, DAO<User> daoUser, DAO<Category> daoCategory, DAO<SettingsEntry> daoSettingsEntry,
			Properties properties, EventBus eventBus) {
		this.eventBus = eventBus;
		this.daoSchoolYear = daoSchoolYear;
		this.daoUser = daoUser;
		this.daoCategory = daoCategory;
		this.daoSettingsEntry = daoSettingsEntry;
		this.currentUser = properties.currentUser;
		this.currentUser.addStateChangeListener(new StateChangeListener() {
			@Override
			public void stateChange(Object value) {
				if (value != null)
					updateUser();
			}
		});
	}

	@AfterVMBinding
	public void initialiseStates() {
		schoolYears.set(new ArrayList<SchoolYear>());
		categories.set(new ArrayList<Category>());
		settingsEntries.set(new ArrayList<SettingsEntry>());
		passwordChangeStatus.set(null);
		userName.set(null);
		userEmail.set(null);
	}
	
	public void refresh() {
		updateSchoolYears();
		updateCategories();
		updateSettingsEntries();
		updateUser();
	}

	private void updateSchoolYears() {
		schoolYears.set(daoSchoolYear.findAll());
	}

	private void updateCategories() {
		categories.set(daoCategory.findAll());
	}

	private void updateSettingsEntries() {
		settingsEntries.set(daoSettingsEntry.findAll());
	}

	private void updateUser() {
		userName.set(currentUser.get().getUsername());
		userEmail.set(currentUser.get().getEmail());
	}

	public void doUpdateSchoolYear(SchoolYear schoolYear) {
		daoSchoolYear.update(schoolYear);
		updateSchoolYears();
	}

	/**
	 * Delets {@link SchoolYear} if it isn't the current one.
	 * 
	 * @param schoolYear
	 */
	public void doDeleteSchoolYear(SchoolYear schoolYear) {
		if (!schoolYear.isActive()) {
			daoSchoolYear.delete(schoolYear);
		} else {
			eventBus.post(new MessageEvent("Löschen nicht möglich!", 
					"Das aktuelle Schuljahr kann nicht gelöscht werden.", Type.WARNING));
		}

		updateSchoolYears();
	}

	public void doUpdateCategory(Category category) {
		if (category.getName().isEmpty()) {
			eventBus.post(new MessageEvent("Speichern nicht möglich!",
					"Das Feld 'Kategorie' darf nicht leer sein.", Type.WARNING));
		} else {
			daoCategory.update(category);
		}

		updateCategories();
	}

	public void doUpdateSettingsEntry(SettingsEntry settingsEntry) {
		daoSettingsEntry.update(settingsEntry);
		updateSettingsEntries();
	}

	public void doDeleteCategory(Category category) {
		if (category.getTeachingMaterials().isEmpty()) {
			daoCategory.delete(category);
		} else {
			eventBus.post(new MessageEvent("Löschen nicht möglich!",
					"Kategorie wird noch verwendet.", Type.WARNING));
		}

		updateCategories();
	}

	/**
	 * Updates {@code userName} and {@code userEmail} of the current {@link User}.<br>
	 * Checks if username or email are already in use by another {@link User}.
	 * 
	 * @param userName
	 * @param userEmail
	 */
	@HandlesAction(DoUpdateUser.class)
	public void doUpdateUser(String userName, String userEmail) {
		if (userEmail.isEmpty())
			userEmail = null;
		
		Collection<User> userWithSameNameOrPassword = daoUser.findAllWithCriteria(
				Restrictions.or(
						Restrictions.eq("username", userName),
						Restrictions.eq("email", userEmail)));
		
		if ((userWithSameNameOrPassword.size() == 1	&& userWithSameNameOrPassword.contains(currentUser.get())) 
					|| userWithSameNameOrPassword.isEmpty()) {
			User user = currentUser.get();
			user.setUsername(userName);
			user.setEmail(userEmail);
			daoUser.update(user);
			this.userName.set(userName);
			this.userEmail.set(userEmail);
			currentUser.notifyAllListeners();
			eventBus.post(new MessageEvent("Daten wurden geändert"));
		} else {
			eventBus.post(new MessageEvent("Speichern fehlgeschlagen!",
					"Es existiert bereits ein Nutzer mit dem Nutzername oder der E-Mail-Adresse.", Type.WARNING));
		}
	}

	/**
	 * Tries to change the password of the currently logged in user. If one of
	 * the parameters is empty, a {@link MessageEvent} is posted to the {@link EventBus}.<br>
	 * If the new password and the new verified password do not match, a
	 * {@link MessageEvent} is posted to the {@link EventBus}.
	 * 
	 * After successfully checking the current password, the password is changed.
	 * 
	 * @param currentPassword
	 *            String with the current password
	 * @param newPassword
	 *            String with the new password
	 * @param newPasswordVerified
	 *            String with the verified new password
	 */
	@HandlesAction(DoPasswordChange.class)
	public void doPasswordChange(String currentPassword, String newPassword, String newPasswordVerified) {
		User user = currentUser.get();

		try {
			// Check if one of the fields is empty or the two new passwords do
			// not match
			if (currentPassword.isEmpty() || newPassword.isEmpty() || newPasswordVerified.isEmpty()) {
				eventBus.post(new MessageEvent("Leere Felder!", "Bitte alle Felder ausfüllen.", Type.WARNING));
			} else if (!newPassword.equals(newPasswordVerified)) {
				eventBus.post(new MessageEvent(
						"Passwörter stimmen nicht überein!", "Die beiden neuen Passwörter stimmen nicht überein.", Type.WARNING));
			} else if (!PasswordHash.validatePassword(currentPassword, user.getPassword())) {
				eventBus.post(new MessageEvent("Falsches Passwort!", "Das aktuelle Passwort ist nicht korrekt.", Type.WARNING));
			} else {
				// Change the password in the database and update the user object
				user.setPassword(PasswordHash.createHash(newPassword));

				daoUser.update(user);
				currentUser.notifyAllListeners();

				// TODO: passwordChangeStatus.notifyAllListeners(); does not work
				passwordChangeStatus.set(null);
				passwordChangeStatus.set(ChangeStatus.SUCCESSFULL);

				eventBus.post(new MessageEvent("Passwort geändert"));
			}

		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
			LOG.warn(e.getMessage());
			eventBus.post(new MessageEvent("Fehler bei der Passwortänderung!", "Bitte kontaktieren Sie einen Entwickler.", Type.WARNING));
		}
	}

	public enum ChangeStatus {
		EMPTY_FIELDS, 
		CURRENT_PASSWORD_WRONG,
		NEW_PASSWORD_NOT_EQUALS, 
		NAME_OR_MAIL_ALREADY_EXISTS, 
		FAILED, 
		SUCCESSFULL;
	}
} public interface Categories extends State<Collection<Category>> {} public interface SettingsEntries extends State<Collection<SettingsEntry>> {} public interface SchoolYears extends State<Collection<SchoolYear>> {} public enum ChangeStatus {
	EMPTY_FIELDS, 
	CURRENT_PASSWORD_WRONG,
	NEW_PASSWORD_NOT_EQUALS, 
	NAME_OR_MAIL_ALREADY_EXISTS, 
	FAILED, 
	SUCCESSFULL;
} public interface UserEmail extends State<String> {} public interface UserName extends State<String> {} public interface DoUpdateUser extends ActionHandler {} public interface DoPasswordChange extends ActionHandler {} public interface PasswordChangeStatus extends State<ChangeStatus> {}
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.NoSuchElementException;
import com.google.common.eventbus.EventBus;
import com.google.inject.Inject;
import com.vaadin.data.Container;
import com.vaadin.data.Property;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.util.BeanItemContainer;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Component;
import com.vaadin.ui.DefaultFieldFactory;
import com.vaadin.ui.Field;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.PasswordField;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.TabSheet.SelectedTabChangeListener;
import com.vaadin.ui.Table;
import com.vaadin.ui.TabSheet.SelectedTabChangeEvent;
import com.vaadin.ui.Table.CellStyleGenerator;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import de.davherrmann.mvvm.BasicState;
import de.davherrmann.mvvm.State;
import de.davherrmann.mvvm.StateChangeListener;
import de.davherrmann.mvvm.ViewModelComposer;
import de.davherrmann.mvvm.annotations.BindAction;
import de.davherrmann.mvvm.annotations.BindState;
import de.dhbw.humbuch.event.ConfirmEvent;
import de.dhbw.humbuch.event.MessageEvent;
import de.dhbw.humbuch.event.MessageEvent.Type;
import de.dhbw.humbuch.model.entity.SchoolYear;
import de.dhbw.humbuch.model.entity.Category;
import de.dhbw.humbuch.model.entity.SettingsEntry;
import de.dhbw.humbuch.viewmodel.SettingsViewModel;
import de.dhbw.humbuch.viewmodel.SettingsViewModel.Categories;
import de.dhbw.humbuch.viewmodel.SettingsViewModel.SettingsEntries;
import de.dhbw.humbuch.viewmodel.SettingsViewModel.SchoolYears;
import de.dhbw.humbuch.viewmodel.SettingsViewModel.ChangeStatus;
import de.dhbw.humbuch.viewmodel.SettingsViewModel.UserEmail;
import de.dhbw.humbuch.viewmodel.SettingsViewModel.UserName;
import de.dhbw.humbuch.viewmodel.SettingsViewModel.DoUpdateUser;
import de.dhbw.humbuch.viewmodel.SettingsViewModel.DoPasswordChange;
import de.dhbw.humbuch.viewmodel.SettingsViewModel.PasswordChangeStatus;

		final Button btnEdit = new Button("Bearbeiten");
		final Button btnCancel = new Button("Abbrechen");
		final Button btnSave = new Button("Speichern");
		
		btnEdit.setEnabled(false);
		btnSave.setVisible(false);
		btnSave.addStyleName("default");
		btnCancel.setVisible(false);

		editButtons.addComponent(btnEdit);
		editButtons.addComponent(btnCancel);
		editButtons.addComponent(btnSave);

		//Add/Delete buttons
		final HorizontalLayout addButtons = new HorizontalLayout();
		addButtons.setSpacing(true);
		
		final Button btnAdd = new Button("Hinzufügen");
		final Button btnDelete = new Button("Löschen");
		
		btnDelete.setEnabled(false);
		
		addButtons.addComponent(btnAdd);
		addButtons.addComponent(btnDelete);

		tab.addComponent(new HorizontalLayout() {
			{
				setWidth("100%");
				setHeight(null);
				addComponent(addButtons);
				addComponent(editButtons);
				setComponentAlignment(editButtons, Alignment.MIDDLE_RIGHT);
			}
		});
		
		yearTable = new Table() {
			private static final long serialVersionUID = 1L;

			@Override
			protected String formatPropertyValue(Object rowId, Object colId,
					Property<?> property) {
				if (!colId.equals(YEAR_YEAR)) {
					SimpleDateFormat df = new SimpleDateFormat();
					df.applyPattern("dd.MM.yyyy");
					if (property.getValue() == null) {
						return null;
					} else {
						return df
								.format(((Date) property.getValue()).getTime());
					}
				}
				return super.formatPropertyValue(rowId, colId, property);
			}
		};

		yearTable.setSizeFull();
		yearTable.setSelectable(true);
		yearTable.setImmediate(true);

		yearTable.setContainerDataSource(yearData);

		yearTable.setVisibleColumns(new Object[] { YEAR_YEAR, YEAR_FROM,
				YEAR_TO, YEAR_END_FIRST, YEAR_BEGIN_SEC });
		yearTable.setColumnHeader(YEAR_YEAR, "Schuljahr");
		yearTable.setColumnHeader(YEAR_FROM, "Beginn");
		yearTable.setColumnHeader(YEAR_TO, "Ende");
		yearTable.setColumnHeader(YEAR_END_FIRST, "Ende 1. Halbjahr");
		yearTable.setColumnHeader(YEAR_BEGIN_SEC, "Begin 2. Halbjahr");

		yearTable.setColumnAlignment(YEAR_FROM, Table.Align.RIGHT);
		yearTable.setColumnAlignment(YEAR_TO, Table.Align.RIGHT);
		yearTable.setColumnAlignment(YEAR_END_FIRST, Table.Align.RIGHT);
		yearTable.setColumnAlignment(YEAR_BEGIN_SEC, Table.Align.RIGHT);

		tab.addComponent(yearTable);
		tab.setExpandRatio(yearTable, 1);

		/*
		 * Listeners
		 */
		// Enable/Disable edit + delete button
		yearTable.addValueChangeListener(new Property.ValueChangeListener() {
			@Override
			public void valueChange(ValueChangeEvent event) {
				SchoolYear item = (SchoolYear) yearTable.getValue();
				btnEdit.setEnabled(item != null);
				btnDelete.setEnabled(item != null);
			}
		});

		// Edit
		btnEdit.addClickListener(new ClickListener() {

			@Override
			public void buttonClick(ClickEvent event) {
				configureEditable(yearTable, new Button[]{btnCancel, btnSave}, new Button[]{btnEdit}, yearFields, true);

			}
		});

		// Cancel
		btnCancel.addClickListener(new ClickListener() {

			@Override
			public void buttonClick(ClickEvent event) {
				discardFields(yearFields);
				configureEditable(yearTable, new Button[]{btnCancel, btnSave}, new Button[]{btnEdit}, yearFields, false);
				schoolYears.notifyAllListeners();
			}
		});

		// Save
		btnSave.addClickListener(new ClickListener() {
			@Override
			public void buttonClick(ClickEvent event) {
				commitFields(yearFields);
				SchoolYear item = (SchoolYear) yearTable.getValue();
				if (item.getYear().isEmpty() || item.getFromDate() == null || item.getToDate() == null || item.getBeginSecondTerm() == null || item.getEndFirstTerm() == null) {
					eventBus.post(new MessageEvent("Speichern nicht möglich!",
							"Alle Felder müssen ausgefüllt sein.", Type.WARNING));