Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package org.azrul.langkuik.framework.webgui; import com.vaadin.data.Property; import com.vaadin.data.Property.ValueChangeListener; import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.fieldgroup.BeanFieldGroup; import com.vaadin.data.fieldgroup.FieldGroup; import com.vaadin.data.fieldgroup.FieldGroup.CommitException; import com.vaadin.data.util.converter.AbstractStringToNumberConverter; import com.vaadin.navigator.Navigator; import com.vaadin.navigator.ViewChangeListener; import com.vaadin.server.Resource; import com.vaadin.server.Sizeable.Unit; import com.vaadin.server.UserError; import com.vaadin.shared.ui.MarginInfo; import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.AbstractTextField; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Component; import com.vaadin.ui.DateField; import com.vaadin.ui.FormLayout; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Notification; import com.vaadin.ui.TabSheet; import com.vaadin.ui.TextField; import com.vaadin.ui.Window; import java.lang.reflect.ParameterizedType; import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import org.azrul.langkuik.annotations.Choice; import org.azrul.langkuik.annotations.EntityUserMap; import org.azrul.langkuik.annotations.WebEntity; import org.azrul.langkuik.annotations.WebField; import org.azrul.langkuik.dao.DataAccessObject; import org.azrul.langkuik.dao.HibernateGenericDAO; import org.azrul.langkuik.framework.PageParameter; import org.azrul.langkuik.framework.activechoice.ActiveChoiceEnum; import org.azrul.langkuik.framework.activechoice.ActiveChoiceTarget; import org.azrul.langkuik.framework.activechoice.ActiveChoiceUtils; import org.azrul.langkuik.framework.activechoice.EmptyEnum; import org.azrul.langkuik.framework.customtype.CustomType; import org.azrul.langkuik.framework.customtype.attachment.AttachmentCustomTypeUICreator; import org.azrul.langkuik.framework.webgui.breadcrumb.BreadCrumbBuilder; import org.azrul.langkuik.framework.webgui.breadcrumb.History; import org.azrul.langkuik.security.constant.ComponentState; import org.azrul.langkuik.security.role.EntityRight; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; public class BeanView<P, C> extends VerticalView { private C currentBean; private P parentBean; private String parentToCurrentBeanField; private final DataAccessObject<C> dao; private final PageParameter pageParameter; public BeanView(C currentBean, P parentBean, String parentToCurrentBeanField, PageParameter pageParameter) { this.currentBean = currentBean; this.pageParameter = pageParameter; this.dao = new HibernateGenericDAO<>(pageParameter.getEntityManagerFactory(), (Class<C>) currentBean.getClass()); this.parentBean = parentBean; this.parentToCurrentBeanField = parentToCurrentBeanField; // this.parentBean = parentBean; } public void setViewResource(String key, Resource resource) { this.setResource(key, resource); } public Resource getViewResource(String key) { return this.getResource(key); } @Override public void enter(final ViewChangeListener.ViewChangeEvent vcevent) { setCurrentView(vcevent.getViewName()); //reset form this.removeAllComponents(); //determine user details UserDetails userDetails = null; Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (!(auth instanceof AnonymousAuthenticationToken)) { userDetails = (UserDetails) auth.getPrincipal(); } else { return; } final Set<String> currentUserRoles = new HashSet<>(); for (GrantedAuthority grantedAuth : userDetails.getAuthorities()) { currentUserRoles.add(grantedAuth.getAuthority()); } //determine entity rights EntityRight entityRight = null; EntityUserMap[] entityUserMaps = ((WebEntity) currentBean.getClass().getAnnotation(WebEntity.class)) .userMap(); for (EntityUserMap e : entityUserMaps) { if (currentUserRoles.contains(e.role()) || ("*").equals(e.role())) { entityRight = e.right(); break; } } if (entityRight == null) { //if entityRight=EntityRight.NONE, still allow to go through because field level might be accessible //Not accessible return; } //create bean utils final BeanUtils beanUtils = new BeanUtils(); //rebuild pageParameter.getBreadcrumb() BreadCrumbBuilder.buildBreadCrumb(vcevent.getNavigator(), pageParameter.getBreadcrumb(), pageParameter.getHistory()); //rebuild components if (currentBean == null) { return; } //refresh current item C newBean = dao.refresh(currentBean); if (newBean != null) { currentBean = newBean; } final BeanFieldGroup fieldGroup = new BeanFieldGroup(currentBean.getClass()); fieldGroup.setItemDataSource(currentBean); final FormLayout form = new FormLayout(); Map<String, Map<Integer, FieldContainer>> groups = beanUtils.createGroupsFromBean(currentBean.getClass()); //render form according to tab if (groups.size() == 1) { createForm(entityRight, currentUserRoles, groups, fieldGroup, pageParameter.getCustomTypeDaos(), vcevent.getNavigator(), form); } else { TabSheet tabSheet = new TabSheet(); for (String group : groups.keySet()) { if (("All").equals(group)) { createForm(entityRight, currentUserRoles, groups, group, fieldGroup, pageParameter.getCustomTypeDaos(), vcevent.getNavigator(), form); } else { FormLayout tab = new FormLayout(); createForm(entityRight, currentUserRoles, groups, group, fieldGroup, pageParameter.getCustomTypeDaos(), vcevent.getNavigator(), tab); tabSheet.addTab(tab, group); } } form.addComponent(tabSheet); } //Navigation and actions HorizontalLayout navButtons = new HorizontalLayout(); navButtons.setSpacing(true); Button saveAndBackBtn = new Button("Save and back", new Button.ClickListener() { @Override public void buttonClick(Button.ClickEvent event) { try { fieldGroup.commit(); currentBean = (C) fieldGroup.getItemDataSource().getBean(); currentBean = saveBean(currentBean, parentBean, beanUtils, currentUserRoles); if (!pageParameter.getHistory().isEmpty()) { String currentView = pageParameter.getHistory().pop().getViewHandle(); String lastView = pageParameter.getHistory().peek().getViewHandle(); vcevent.getNavigator().removeView(currentView); vcevent.getNavigator().navigateTo(lastView); } } catch (FieldGroup.CommitException ex) { handleFieldsError(fieldGroup); } } }); navButtons.addComponent(saveAndBackBtn); saveAndBackBtn.setId(saveAndBackBtn.getCaption()); form.addComponent(navButtons); form.setMargin(new MarginInfo(true)); this.addComponent(form); } private void createForm(EntityRight entityRight, final Set<String> currentUserRoles, final Map<String, Map<Integer, FieldContainer>> groups, final BeanFieldGroup fieldGroup, final List<DataAccessObject<?>> customTypeDaos, Navigator nav, final FormLayout form) throws FieldGroup.BindException, UnsupportedOperationException { createForm(entityRight, currentUserRoles, groups, null, fieldGroup, customTypeDaos, nav, form); } private void createForm(EntityRight entityRight, final Set<String> currentUserRoles, final Map<String, Map<Integer, FieldContainer>> groups, String group, final BeanFieldGroup fieldGroup, final List<DataAccessObject<?>> customTypeDaos, final Navigator nav, final FormLayout form) throws FieldGroup.BindException, UnsupportedOperationException { //create bean utils final BeanUtils beanUtils = new BeanUtils(); //select which group we want Map<Integer, FieldContainer> fieldContainerMap = null; if (group == null) { fieldContainerMap = groups.entrySet().iterator().next().getValue(); } else { fieldContainerMap = groups.get(group); } //collect all activechoices Map<com.vaadin.ui.ComboBox, ActiveChoiceTarget> activeChoicesWithFieldAsKey = new HashMap<>(); Map<String, com.vaadin.ui.ComboBox> activeChoicesFieldWithHierarchyAsKey = new HashMap<>(); //collect all cutsom types List<Class> customTypes = new ArrayList<>(); for (DataAccessObject<?> ctDao : customTypeDaos) { customTypes.add(ctDao.getType()); } //deal with every field everyField: for (Map.Entry<Integer, FieldContainer> entry : fieldContainerMap.entrySet()) { final FieldContainer fieldContainer = entry.getValue(); final ComponentState effectiveFieldState = beanUtils .calculateEffectiveComponentState(fieldContainer.getPojoField(), currentUserRoles, entityRight); //Create form if (ComponentState.INVISIBLE.equals(effectiveFieldState)) { continue everyField; //Continue with next field } //deal with normal form element com.vaadin.ui.Field uifield = null; //deal with plain choices if (fieldContainer.getWebField().choices().length > 0) { //deal with choices com.vaadin.ui.ComboBox formComboBox = new com.vaadin.ui.ComboBox( fieldContainer.getWebField().name()); formComboBox.setImmediate(true); fieldGroup.bind(formComboBox, fieldContainer.getPojoField().getName()); for (Choice choice : fieldContainer.getWebField().choices()) { if (choice.value() == -1) { formComboBox.addItem(choice.textValue()); formComboBox.setItemCaption(choice.textValue(), choice.display()); } else { formComboBox.addItem(choice.value()); formComboBox.setItemCaption(choice.value(), choice.display()); } } form.addComponent(formComboBox); uifield = formComboBox; //deal with active choices } else if (fieldContainer.getWebField().activeChoice().enumTree() != EmptyEnum.class) { //collect active choices - com.vaadin.ui.ComboBox formComboBox = new com.vaadin.ui.ComboBox( fieldContainer.getWebField().name()); formComboBox.setImmediate(true); fieldGroup.bind(formComboBox, fieldContainer.getPojoField().getName()); String hierarchy = fieldContainer.getWebField().activeChoice().hierarchy(); Class<ActiveChoiceEnum> enumTree = (Class<ActiveChoiceEnum>) fieldContainer.getWebField() .activeChoice().enumTree(); ActiveChoiceTarget activeChoiceTarget = ActiveChoiceUtils.build(enumTree, hierarchy); for (String choice : activeChoiceTarget.getSourceChoices()) { formComboBox.addItem(choice); activeChoicesWithFieldAsKey.put(formComboBox, activeChoiceTarget); activeChoicesFieldWithHierarchyAsKey.put(hierarchy, formComboBox); } form.addComponent(formComboBox); uifield = formComboBox; //deal with relationship } else if (fieldContainer.getPojoField().isAnnotationPresent(OneToMany.class) || fieldContainer.getPojoField().isAnnotationPresent(ManyToOne.class) || fieldContainer.getPojoField().isAnnotationPresent(ManyToMany.class)) { //special relationship: deal with custom type form element int state = 0; Class classOfField = null; while (true) { if (state == 0) { if (Collection.class.isAssignableFrom(fieldContainer.getPojoField().getType())) { classOfField = (Class) ((ParameterizedType) fieldContainer.getPojoField() .getGenericType()).getActualTypeArguments()[0]; state = 1; } else { state = 3; break; } } if (state == 1) { if (CustomType.class.isAssignableFrom(classOfField)) { state = 2; break; } else { state = 3; break; } } } if (state == 2) { //Custom type Button openCustom = new Button("Manage " + fieldContainer.getWebField().name(), new Button.ClickListener() { @Override public void buttonClick(ClickEvent event) { try { fieldGroup.commit(); currentBean = (C) fieldGroup.getItemDataSource().getBean(); currentBean = saveBean(currentBean, parentBean, beanUtils, currentUserRoles); fieldGroup.setItemDataSource(currentBean); //field class Class iclassOfField = (Class) ((ParameterizedType) fieldContainer .getPojoField().getGenericType()).getActualTypeArguments()[0]; //find a custom type dao DataAccessObject<? extends CustomType> chosenCTDao = null; for (DataAccessObject cdao : customTypeDaos) { if (cdao.getType().isAssignableFrom(iclassOfField)) { chosenCTDao = cdao; break; } } //deal with windows final Window window = new Window(); final AttachmentCustomTypeUICreator<C> attachmentCustomTypeUICreator = new AttachmentCustomTypeUICreator(); Component customTypeComponent = attachmentCustomTypeUICreator .createUIForForm(currentBean, iclassOfField, fieldContainer.getPojoField().getName(), BeanView.this, dao, chosenCTDao, pageParameter.getRelationManagerFactory(), pageParameter.getConfig(), effectiveFieldState, window); customTypeComponent .setCaption("Manage " + fieldContainer.getWebField().name()); customTypeComponent.setId(customTypeComponent.getCaption()); window.setCaption(customTypeComponent.getCaption()); window.setId(window.getCaption()); window.setContent(customTypeComponent); window.setModal(true); BeanView.this.getUI().addWindow(window); } catch (CommitException ex) { handleFieldsError(fieldGroup); } } }); openCustom.setId(openCustom.getCaption()); form.addComponent(openCustom); } else { //relationship try { fieldContainer.getPojoField().setAccessible(true); final WebField webField = fieldContainer.getPojoField().getAnnotation(WebField.class); Button relationshipButton = null; if (fieldContainer.getPojoField().isAnnotationPresent(OneToMany.class)) { relationshipButton = new Button("Manage " + webField.name(), new Button.ClickListener() { @Override public void buttonClick(Button.ClickEvent event) { try { fieldGroup.commit(); currentBean = (C) fieldGroup.getItemDataSource().getBean(); currentBean = saveBean(currentBean, parentBean, beanUtils, currentUserRoles); Class classOfField = (Class) ((ParameterizedType) fieldContainer .getPojoField().getGenericType()) .getActualTypeArguments()[0]; EditorTableView view = new EditorTableView(currentBean, fieldContainer.getPojoField().getName(), effectiveFieldState, classOfField, ChoiceType.CHOOSE_MANY, pageParameter); String targetView = "BEAN_VIEW_" + UUID.randomUUID().toString(); History his = new History(targetView, "Manage " + webField.name()); pageParameter.getHistory().push(his); nav.addView(targetView, view); nav.navigateTo(targetView); } catch (CommitException ex) { handleFieldsError(fieldGroup); } } }); } else if (fieldContainer.getPojoField().isAnnotationPresent(ManyToOne.class)) { relationshipButton = new Button("Manage " + webField.name(), new Button.ClickListener() { @Override public void buttonClick(Button.ClickEvent event) { try { fieldGroup.commit(); currentBean = (C) fieldGroup.getItemDataSource().getBean(); fieldGroup.commit(); currentBean = (C) fieldGroup.getItemDataSource().getBean(); currentBean = saveBean(currentBean, parentBean, beanUtils, currentUserRoles); EditorTableView view = new EditorTableView(currentBean, fieldContainer.getPojoField().getName(), effectiveFieldState, fieldContainer.getPojoField().getType(), ChoiceType.CHOOSE_ONE, pageParameter); String targetView = "BEAN_VIEW_" + UUID.randomUUID().toString(); History his = new History(targetView, "Manage " + webField.name()); pageParameter.getHistory().push(his); nav.addView(targetView, view); nav.navigateTo(targetView); } catch (CommitException ex) { handleFieldsError(fieldGroup); } } }); } else if (fieldContainer.getPojoField().isAnnotationPresent(ManyToMany.class)) { relationshipButton = new Button("Manage " + webField.name(), new Button.ClickListener() { @Override public void buttonClick(Button.ClickEvent event) { try { fieldGroup.commit(); currentBean = (C) fieldGroup.getItemDataSource().getBean(); fieldGroup.commit(); currentBean = (C) fieldGroup.getItemDataSource().getBean(); currentBean = saveBean(currentBean, parentBean, beanUtils, currentUserRoles); Class classOfField = (Class) ((ParameterizedType) fieldContainer .getPojoField().getGenericType()) .getActualTypeArguments()[0]; EditorTableView view = new EditorTableView(currentBean, fieldContainer.getPojoField().getName(), effectiveFieldState, classOfField, ChoiceType.CHOOSE_MANY, pageParameter); String targetView = "BEAN_VIEW_" + UUID.randomUUID().toString(); History his = new History(targetView, "Manage " + webField.name()); pageParameter.getHistory().push(his); nav.addView(targetView, view); nav.navigateTo(targetView); } catch (CommitException ex) { handleFieldsError(fieldGroup); } } }); } relationshipButton.setId(relationshipButton.getCaption()); form.addComponent(relationshipButton); } catch (IllegalArgumentException ex) { Logger.getLogger(BeanView.class.getName()).log(Level.SEVERE, null, ex); } } //deal with id } else if (fieldContainer.getPojoField().isAnnotationPresent(Id.class)) { com.vaadin.ui.Field formField = fieldGroup.buildAndBind(fieldContainer.getWebField().name(), fieldContainer.getPojoField().getName()); if (Number.class.isAssignableFrom(fieldContainer.getPojoField().getType())) { ((TextField) formField).setConverter( new NumberBasedIDConverter((Class<Number>) fieldContainer.getPojoField().getType())); } form.addComponent(formField); uifield = formField; //deal with nominal form element } else { com.vaadin.ui.Field formField = fieldGroup.buildAndBind(fieldContainer.getWebField().name(), fieldContainer.getPojoField().getName()); if (fieldContainer.getPojoField().getType().equals(Date.class)) { //deal with date DateField dateField = (DateField) formField; dateField.setDateFormat(pageParameter.getConfig().get("dateFormat")); dateField.setWidth(100f, Unit.PIXELS); } else if (fieldContainer.getPojoField().getType().equals(BigDecimal.class)) { TextField bdField = (TextField) formField; bdField.setConverter(new StringToBigDecimalConverter()); } form.addComponent(formField); uifield = formField; } if (uifield != null) { //deal with read only if (ComponentState.READ_ONLY.equals(effectiveFieldState)) { uifield.setReadOnly(true); } else { uifield.setReadOnly(false); if (fieldContainer.getWebField().required() == true) { uifield.setRequired(true); } } //set null presentation if (uifield instanceof AbstractTextField) { AbstractTextField textField = (AbstractTextField) uifield; textField.setNullRepresentation(""); } //set debug id uifield.setId(uifield.getCaption()); } } //deal with active choice for (final com.vaadin.ui.ComboBox sourceField : activeChoicesWithFieldAsKey.keySet()) { final ActiveChoiceTarget target = activeChoicesWithFieldAsKey.get(sourceField); final com.vaadin.ui.ComboBox targetField = activeChoicesFieldWithHierarchyAsKey .get(target.getTargetHierarchy()); sourceField.addValueChangeListener(new ValueChangeListener() { @Override public void valueChange(Property.ValueChangeEvent event) { List<String> targetValues = target.getTargets().get(sourceField.getValue()); if (targetValues != null && !targetValues.isEmpty() && targetField != null) { targetField.removeAllItems(); for (String targetValue : targetValues) { targetField.addItem(targetValue); } } } }); } } private C saveBean(C bean, P pbean, BeanUtils beanUtils, Set<String> currentUserRoles) { if (beanUtils.isEditable(bean.getClass(), currentUserRoles) || beanUtils.isCreatable(bean.getClass(), currentUserRoles)) { if (pbean == null) { return dao.save(bean); } else { return dao.saveAndAssociate(bean, pbean, parentToCurrentBeanField, pageParameter.getRelationManagerFactory().create((Class<P>) pbean.getClass(), (Class<C>) bean.getClass())); } } else { return null; } } private List<com.vaadin.ui.Field<?>> validateFields(BeanFieldGroup fg) { List<com.vaadin.ui.Field<?>> invFields = new ArrayList<>(); Collection<com.vaadin.ui.Field<?>> fields = fg.getFields(); for (com.vaadin.ui.Field f : fields) { try { f.validate(); } catch (InvalidValueException ivex) { invFields.add(f); } } return invFields; } private void handleFieldsError(BeanFieldGroup fieldGroup) { List<com.vaadin.ui.Field<?>> invFields = validateFields(fieldGroup); if (invFields.size() > 0) { invFields.iterator().next().focus(); for (com.vaadin.ui.Field<?> invf : invFields) { if (invf instanceof AbstractComponent) { ((AbstractComponent) invf).setComponentError(new UserError("Invalid value")); } } } Notification.show("Invalid value in a field", Notification.Type.HUMANIZED_MESSAGE); } } class StringToBigDecimalConverter extends AbstractStringToNumberConverter<BigDecimal> { @Override protected NumberFormat getFormat(Locale locale) { NumberFormat numberFormat = super.getFormat(locale); if (numberFormat instanceof DecimalFormat) { ((DecimalFormat) numberFormat).setParseBigDecimal(true); } return numberFormat; } @Override public BigDecimal convertToModel(String value, Class<? extends BigDecimal> targetType, Locale locale) throws com.vaadin.data.util.converter.Converter.ConversionException { return (BigDecimal) convertToNumber(value, BigDecimal.class, locale); } @Override public Class<BigDecimal> getModelType() { return BigDecimal.class; } }