Java tutorial
/* * Copyright (C) 2014-2016, Falko Brutigam. All rights reserved. * * This is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software * Foundation; either version 3.0 of the License, or (at your option) any later * version. * * This software is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */ package org.polymap.wbv.ui; import static com.google.common.collect.ImmutableList.copyOf; import static com.google.common.collect.Iterables.transform; import static java.util.Arrays.asList; import static org.polymap.core.ui.FormDataFactory.on; import static org.polymap.model2.query.Expressions.anyOf; import static org.polymap.model2.query.Expressions.eq; import static org.polymap.model2.query.Expressions.is; import static org.polymap.model2.query.Expressions.notEq; import static org.polymap.rhei.field.Validators.AND; import static org.polymap.wbv.ui.PropertyAdapter.descriptorFor; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.beans.PropertyChangeEvent; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.FastDateFormat; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.google.common.base.Function; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.core.runtime.IProgressMonitor; import org.polymap.core.runtime.event.EventHandler; import org.polymap.core.runtime.event.EventManager; import org.polymap.core.ui.FormLayoutFactory; import org.polymap.rhei.batik.BatikApplication; import org.polymap.rhei.batik.Context; import org.polymap.rhei.batik.IAppContext; import org.polymap.rhei.batik.PanelChangeEvent; import org.polymap.rhei.batik.PanelChangeEvent.EventType; import org.polymap.rhei.batik.PanelSite; import org.polymap.rhei.batik.Scope; import org.polymap.rhei.batik.app.SvgImageRegistryHelper; import org.polymap.rhei.batik.toolkit.SimpleDialog; import org.polymap.rhei.batik.toolkit.Snackbar.Appearance; import org.polymap.rhei.field.IFormFieldValidator; import org.polymap.rhei.field.NotEmptyValidator; import org.polymap.rhei.field.NumberValidator; import org.polymap.rhei.field.PicklistFormField; import org.polymap.rhei.field.StringFormField; import org.polymap.rhei.table.ActionCellEditor; import org.polymap.rhei.table.DefaultTableValidator; import org.polymap.rhei.table.DelegatingValidator; import org.polymap.rhei.table.FeatureTableViewer; import org.polymap.rhei.table.FormFeatureTableColumn; import org.polymap.rhei.table.IFeatureTableColumn; import org.polymap.rhei.table.IFeatureTableElement; import org.polymap.model2.query.Expressions; import org.polymap.model2.query.ResultSet; import org.polymap.model2.runtime.UnitOfWork; import org.polymap.wbv.WbvPlugin; import org.polymap.wbv.model.Flurstueck; import org.polymap.wbv.model.Gemarkung; import org.polymap.wbv.model.Revier; import org.polymap.wbv.model.Waldbesitzer; import org.polymap.wbv.ui.CompositesFeatureContentProvider.FeatureTableElement; /** * * * @author <a href="http://www.polymap.de">Falko Brutigam</a> */ public class FlurstueckTableViewer extends FeatureTableViewer { private static final Log log = LogFactory.getLog(FlurstueckTableViewer.class); private static final FastDateFormat df = FastDateFormat.getInstance("dd.MM.yyyy"); private Context<UnitOfWork> uow; private Context<Waldbesitzer> wb; private Context<Flurstueck> selected; /** */ @Scope("org.polymap.wbv.ui") protected Context<Revier> revier; private PanelSite panelSite; private boolean flurstueckDeleted; private Object panelChangeListener = new Object() { @EventHandler(display = true) protected void fieldChange(PanelChangeEvent ev) { if (!getTable().isDisposed()) { refresh(); } else { IAppContext context = BatikApplication.instance().getContext(); context.removeListener(panelChangeListener); } } }; public boolean isDirty() { boolean anyColumnDirty = displayed.values().stream().filter(col -> ((FormFeatureTableColumn) col).isDirty()) .findAny().isPresent(); return anyColumnDirty || flurstueckDeleted; } public boolean isValid() { return !displayed.values().stream().filter(col -> !((FormFeatureTableColumn) col).isValid()).findAny() .isPresent(); } public void submit(IProgressMonitor monitor) throws Exception { for (IFeatureTableColumn col : displayed.values()) { FormFeatureTableColumn fcol = (FormFeatureTableColumn) col; // aenderung datum for (IFeatureTableElement modified : fcol.modified().keySet()) { Flurstueck fst = FeatureTableElement.entity(modified); fst.aenderung.set(new Date()); } // submit fcol.submit(monitor); } } @EventHandler(display = true) protected void fieldChange(PropertyChangeEvent ev) { log.info("isDirty: " + isDirty() + ", isValid: " + isValid()); } public FlurstueckTableViewer(PanelSite panelSite, Composite parent) { super(parent, /* SWT.VIRTUAL | SWT.V_SCROLL | SWT.FULL_SELECTION |*/ SWT.BORDER); this.panelSite = panelSite; IAppContext context = BatikApplication.instance().getContext(); context.propagate(this); suppressSelection(); context.addListener(panelChangeListener, ev -> ev.getType().isOnOf(EventType.LIFECYCLE)); // listen to column/field changes EventManager.instance().subscribe(this, ev -> ev instanceof PropertyChangeEvent && displayed.values().contains(ev.getSource())); try { // suppress deferred loading to fix "empty table" issue // setContent( fs.getFeatures( this.baseFilter ) ); setContentProvider(new CompositesFeatureContentProvider()); // Action: delete addColumn(new FormFeatureTableColumn(descriptorFor("", String.class)).setWeight(1, 25) .setLabelProvider(new ColumnLabelProvider() { @Override public Image getImage(Object elm) { return WbvPlugin.images().svgImage("delete.svg", SvgImageRegistryHelper.NORMAL12); } @Override public String getText(Object elm) { return null; } @Override public String getToolTipText(Object elm) { return "Flurstck lschen"; } }).setEditing(new ActionCellEditor(elm -> { assert wb.isPresent(); Flurstueck fst = FeatureTableElement.entity(elm); fst.geloescht.set(true); refresh(); flurstueckDeleted = true; fieldChange(null); // waldbesitzer lschen, wenn (wirklich) kein flurstck mehr da if (wb.get().flurstuecke(null).isEmpty()) { uow.get().removeEntity(wb.get()); panelSite.toolkit().createSnackbar(Appearance.FadeIn, "Waldbesitzer wird beim Speichern <b>gelscht</b>!"); } }))); // Action: transfer addColumn(new FormFeatureTableColumn(descriptorFor("", String.class)).setWeight(1, 30) .setLabelProvider(new ColumnLabelProvider() { @Override public Image getImage(Object elm) { return WbvPlugin.images().svgImage("transfer.svg", SvgImageRegistryHelper.NORMAL12); } @Override public String getText(Object elm) { return null; } @Override public String getToolTipText(Object elm) { return "Eigentumsbergang: an einem anderen Waldbesitzer bertragen"; } }).setEditing(new ActionCellEditor(elm -> { Flurstueck fst = FeatureTableElement.entity(elm); selected.set(fst); BatikApplication.instance().getContext().openPanel(panelSite.path(), EigentumsuebergangPanel.ID); }))); // Gemarkung String propName = Flurstueck.TYPE.gemarkung.info().getName(); addColumn(new FormFeatureTableColumn(descriptorFor(propName, Gemarkung.class)).setWeight(6, 80) .setLabelsAndValidation(new DefaultTableValidator<Object, Gemarkung>() { @Override public Object transform2Field(Gemarkung gmk, ValidatorSite site) throws Exception { if (site.isEditing()) { return gmk; } else { //Flurstueck flst = FeatureTableElement.entity( site.element() ); String result = gmk != null ? gmk.label() : "-keine Gemarkung-"; //"WVK: " + flst.wvkGemarkung.get() + "/" + flst.wvkGemeinde.get(); return StringUtils.abbreviate(result, 30); } } @Override public Gemarkung transform2Model(Object fieldValue, ValidatorSite site) throws Exception { return fieldValue != null ? uow.get().entity((Gemarkung) fieldValue) : null; } }).setEditing(new PicklistFormField(Gemarkung.all.get())) .setSortable(new Comparator<IFeatureTableElement>() { public int compare(IFeatureTableElement e1, IFeatureTableElement e2) { return label(e1).compareTo(label(e2)); } protected String label(IFeatureTableElement elm) { Gemarkung gmk = (Gemarkung) elm.getValue(propName); return gmk != null ? gmk.label() : ""; } })).sort(SWT.DOWN); // Flurstcksnummer addColumn(new FormFeatureTableColumn(descriptorFor(Flurstueck.TYPE.zaehlerNenner)).setWeight(3, 50) .setHeader("Nr.") .setLabelsAndValidation(AND(new NotEmptyValidator().forTable(), new AenderungValidator(), new NummerValidator(), new FlurstueckExistsValidator())) .setEditing(new StringFormField())); // Flche NumberValidator flaecheValidator = new NumberValidator(Double.class, Locale.GERMANY, 10, 4, 1, 4); addColumn(new FormFeatureTableColumn(descriptorFor(Flurstueck.TYPE.flaeche)).setWeight(3, 50) .setHeader("Flche\n(in ha)") .setLabelsAndValidation(AND(flaecheValidator.forTable(), new AenderungValidator())) .setEditing(new StringFormField()).setSortable(true)); // davon Wald addColumn(new FormFeatureTableColumn(descriptorFor(Flurstueck.TYPE.flaecheWald)).setWeight(3, 50) .setHeader("Wald\n(in ha)") .setLabelsAndValidation( AND(new WaldflaecheValidator(flaecheValidator), new AenderungValidator())) .setEditing(new StringFormField()).setSortable(true)); // nderungsdatum addColumn(new FormFeatureTableColumn(descriptorFor(Flurstueck.TYPE.aenderung)).setWeight(4, 80) .setHeader("nderung").setLabelsAndValidation(new DateValidator().forTable())); // Bemerkung FormFeatureTableColumn bcolumn = new FormFeatureTableColumn(descriptorFor(Flurstueck.TYPE.bemerkung)); bcolumn.setWeight(11, 120); bcolumn.setLabelsAndValidation(new AenderungValidator()); bcolumn.setEditing(new CellEditor() { private String value; private Text txt; @Override protected Control createControl(Composite _parent) { return null; } @Override protected void doSetFocus() { } @Override protected Object doGetValue() { return value; } @Override protected void doSetValue(Object newValue) { this.value = (String) newValue; } @Override public void activate() { Shell appShell = BatikApplication.shellToParentOn(); new SimpleDialog().centerOn.put(appShell).title.put("Bemerkung").setContents(dialogParent -> { dialogParent.setLayout(FormLayoutFactory.defaults().create()); txt = on(new Text(dialogParent, SWT.MULTI | SWT.WRAP | SWT.BORDER)).fill().width(350) .height(150).control(); txt.setText(value != null ? value : ""); txt.setFocus(); }).addOkAction("BERNEHMEN", () -> { value = txt.getText(); fireApplyEditorValue(); EventManager.instance().publish(new PropertyChangeEvent(bcolumn, "bemerkung", value, null)); return null; }).addCancelAction("ABBRECHEN").open(); } }); addColumn(bcolumn); setInput(wb.get().flurstuecke(revier.get())); // /* Register for property change events */ // EventManager.instance().subscribe( this, new EventFilter<PropertyChangeEvent>() { // public boolean apply( PropertyChangeEvent input ) { // return input.getSource() instanceof Flurstueck; // } // }); } catch (Exception e) { throw new RuntimeException(e); } } // @EventHandler(display=true, delay=1000, scope=Event.Scope.JVM) // protected void entityChanged( List<PropertyChangeEvent> ev ) { // if (!getControl().isDisposed()) { // refresh( true ); // } // } public List<Flurstueck> getSelected() { return copyOf(transform(asList(getSelectedElements()), new Function<IFeatureTableElement, Flurstueck>() { public Flurstueck apply(IFeatureTableElement input) { return (Flurstueck) ((FeatureTableElement) input).getComposite(); } })); } /** * Deprecated: done by {@link FlurstueckTableViewer#submit(IProgressMonitor)}: */ protected class AenderungValidator extends DefaultTableValidator { @Override public Object transform2Model(Object fieldValue, ValidatorSite site) throws Exception { // site.setColumnValue( Flurstueck.TYPE.aenderung.info().getName(), new Date() ); return super.transform2Model(fieldValue, site); } } /** * Im Freistaat Sachsen gibt folgende Flurstckstypen: * <pre> * nur Zhler (Bsp.: 111) * Zhler/[Nenner] ohne Leerzeichen (Bsp.: 111/2) * ZhlerKleinbuchstabe ohne Leerzeichen (Bsp.: 111a) * </pre> */ protected static class NummerValidator extends DefaultTableValidator { public static final Pattern pattern = Pattern.compile("[0-9]+(/[0-9]+|[a-z])?"); @Override public String validate(Object value, ValidatorSite site) { Matcher matcher = pattern.matcher((String) value); return matcher.matches() ? null : "Flurstcksnummern: 111, 111a, 111/2"; } } /** * */ protected class FlurstueckExistsValidator extends DefaultTableValidator { @Override public String validate(Object fieldValue, ValidatorSite site) { Optional<Gemarkung> gmk = site.columnValue(Flurstueck.TYPE.gemarkung.info().getName()); if (!gmk.isPresent()) { return "Noch keine Gemarkung"; } else { ResultSet<Waldbesitzer> rs = uow.get().query(Waldbesitzer.class) .where(anyOf(Waldbesitzer.TYPE.flurstuecke, Expressions.and(notEq(Flurstueck.TYPE.geloescht, true), is(Flurstueck.TYPE.gemarkung, gmk.get()), eq(Flurstueck.TYPE.zaehlerNenner, (String) fieldValue)))) .maxResults(1).execute(); return rs.size() == 0 ? null : "Zhler/Nenner existiert bereits in " + gmk.get().label(); } } } /** * */ protected class WaldflaecheValidator extends DelegatingValidator<String, Double> { public WaldflaecheValidator(IFormFieldValidator<String, Double> delegate) { super(delegate); } @Override public String validate(String fieldValue, ValidatorSite site) { try { Optional<Double> flaeche = site.columnValue(Flurstueck.TYPE.flaeche.info().getName()); Double neueWaldflaeche = (Double) super.transform2Model(fieldValue, site); return flaeche.isPresent() && neueWaldflaeche > flaeche.get() ? "Die Waldflche ist grer als die Gesamtflche" : null; } catch (Exception e) { log.warn("", e); return "Fehler beim validieren der Eingabe: " + e.getLocalizedMessage(); } } } }