org.kalypso.model.wspm.pdb.wspm.CheckinStatePdbOperation.java Source code

Java tutorial

Introduction

Here is the source code for org.kalypso.model.wspm.pdb.wspm.CheckinStatePdbOperation.java

Source

/*----------------    FILE HEADER KALYPSO ------------------------------------------
 *
 *  This file is part of kalypso.
 *  Copyright (C) 2004 by:
 *
 *  Technical University Hamburg-Harburg (TUHH)
 *  Institute of River and coastal engineering
 *  Denickestrae 22
 *  21073 Hamburg, Germany
 *  http://www.tuhh.de/wb
 *
 *  and
 *
 *  Bjoernsen Consulting Engineers (BCE)
 *  Maria Trost 3
 *  56070 Koblenz, Germany
 *  http://www.bjoernsen.de
 *
 *  This library 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 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  Contact:
 *
 *  E-Mail:
 *  belger@bjoernsen.de
 *  schlienger@bjoernsen.de
 *  v.doemming@tuhh.de
 *
 *  ---------------------------------------------------------------------------*/
package org.kalypso.model.wspm.pdb.wspm;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.hibernate.Session;
import org.kalypso.contribs.eclipse.core.runtime.IStatusCollector;
import org.kalypso.contribs.eclipse.core.runtime.StatusCollector;
import org.kalypso.gmlschema.annotation.IAnnotation;
import org.kalypso.model.wspm.core.gml.IProfileFeature;
import org.kalypso.model.wspm.core.profil.IProfile;
import org.kalypso.model.wspm.core.profil.IProfileObject;
import org.kalypso.model.wspm.core.profil.IProfileObjectRecords;
import org.kalypso.model.wspm.core.profil.impl.GenericProfileHorizon;
import org.kalypso.model.wspm.pdb.connect.PdbConnectException;
import org.kalypso.model.wspm.pdb.db.mapping.CrossSection;
import org.kalypso.model.wspm.pdb.db.mapping.CrossSectionPart;
import org.kalypso.model.wspm.pdb.db.mapping.CrossSectionPartParameter;
import org.kalypso.model.wspm.pdb.db.mapping.CrossSectionPartType;
import org.kalypso.model.wspm.pdb.db.mapping.Document;
import org.kalypso.model.wspm.pdb.db.mapping.Event;
import org.kalypso.model.wspm.pdb.db.mapping.Point;
import org.kalypso.model.wspm.pdb.db.mapping.State;
import org.kalypso.model.wspm.pdb.db.mapping.WaterBody;
import org.kalypso.model.wspm.pdb.db.utils.CrossSectionPartTypes;
import org.kalypso.model.wspm.pdb.gaf.GafKind;
import org.kalypso.model.wspm.pdb.gaf.GafPartsMapping;
import org.kalypso.model.wspm.pdb.gaf.GafPointCode;
import org.kalypso.model.wspm.pdb.gaf.ICoefficients;
import org.kalypso.model.wspm.pdb.internal.WspmPdbCorePlugin;
import org.kalypso.model.wspm.pdb.internal.i18n.Messages;
import org.kalypso.model.wspm.pdb.internal.utils.PDBNameGenerator;
import org.kalypso.model.wspm.pdb.internal.wspm.BridgeIdHelper;
import org.kalypso.model.wspm.pdb.internal.wspm.CheckinPartOperation;
import org.kalypso.model.wspm.tuhh.core.IWspmTuhhConstants;
import org.kalypso.model.wspm.tuhh.core.profile.profileobjects.building.BuildingBruecke;
import org.kalypso.model.wspm.tuhh.core.profile.profileobjects.building.BuildingEi;
import org.kalypso.model.wspm.tuhh.core.profile.profileobjects.building.BuildingKreis;
import org.kalypso.model.wspm.tuhh.core.profile.profileobjects.building.BuildingMaul;
import org.kalypso.model.wspm.tuhh.core.profile.profileobjects.building.BuildingWehr;
import org.kalypsodeegree_impl.model.feature.FeatureHelper;
import org.kalypsodeegree_impl.model.geometry.JTSAdapter;

/**
 * @author Gernot Belger
 */
public class CheckinStatePdbOperation implements ICheckinStatePdbOperation {
    public static final String STR_FAILED_TO_CONVERT_GEOMETRY = Messages.getString("CheckinStatePdbOperation.0"); //$NON-NLS-1$

    private final IStatusCollector m_log = new StatusCollector(WspmPdbCorePlugin.PLUGIN_ID);

    private final PDBNameGenerator m_sectionNames = new PDBNameGenerator();

    private final boolean m_checkSectionNames;

    private final CheckinStateOperationData m_data;

    private IProgressMonitor m_monitor;

    /** if true, the records of culvert objects are recreated form the culvert metadata. Only used for GAF export; else we want to keep the original data */
    private final boolean m_updateCulvertObjects;

    /**
     * @param dbSrs
     *          The coordinate system of the database
     */
    public CheckinStatePdbOperation(final CheckinStateOperationData data, final boolean checkSectionNames,
            final boolean updateCulvertObjects) {
        m_data = data;

        m_checkSectionNames = checkSectionNames;
        m_updateCulvertObjects = updateCulvertObjects;
    }

    @Override
    public void setMonitor(final IProgressMonitor monitor) {
        m_monitor = monitor;
    }

    @Override
    public String getLabel() {
        return Messages.getString("CheckinStateOperation.1"); //$NON-NLS-1$
    }

    @Override
    public void execute(final Session session) throws PdbConnectException {
        final IProfileFeature[] profiles = m_data.getProfiles();

        m_monitor.beginTask(Messages.getString("CheckinStatePdbOperation.2"), 10 + profiles.length); //$NON-NLS-1$

        m_monitor.subTask(Messages.getString("CheckinStatePdbOperation.3")); //$NON-NLS-1$

        final State state = saveOrUpdateState(session);
        m_monitor.worked(10);

        final CheckinDocumentWorker worker = new CheckinDocumentWorker(m_data.getDocumentBase());
        worker.createDocuments(session, state, m_data.getReach());

        final WaterBody waterBody = m_data.getWaterBody();

        final EventUploadProvider eventProvider = session == null ? null : new EventUploadProvider(session);

        for (final IProfileFeature feature : profiles) {
            final String label = FeatureHelper.getAnnotationValue(feature, IAnnotation.ANNO_LABEL);
            m_monitor.subTask(String.format(Messages.getString("CheckinStatePdbOperation.4"), label)); //$NON-NLS-1$
            uploadProfile(session, waterBody, state, feature, eventProvider);
            m_monitor.worked(1);
        }

        final IStatus classStatus = m_data.checkClasses();
        if (!classStatus.isOK())
            m_log.add(classStatus);

        m_monitor.subTask(Messages.getString("CheckinStatePdbOperation.5")); //$NON-NLS-1$
    }

    private State saveOrUpdateState(final Session session) {
        final State state = m_data.getState();
        state.setEditingUser(m_data.getUsername());
        state.setEditingDate(new Date());

        /* if state already contains cross sections or documents: delete everything */
        // do not delete existing events, they do not get uploaded via this mechanism
        final Set<CrossSection> crossSections = state.getCrossSections();
        for (final CrossSection crossSection : crossSections)
            session.delete(crossSection);
        crossSections.clear();

        final Set<Document> documents = state.getDocuments();
        for (final Document document : documents)
            session.delete(document);
        documents.clear();

        if (session == null)
            return state;

        /* add state to db if it is new */
        if (!session.contains(state))
            session.save(state);
        else
            session.persist(state);

        // FIXME: necessary to prevent problems with same cross sectionsre-checked in; else db whines about duplicate name
        // check if this breaks the transaction
        session.flush();

        // REMARK: nothing to do if already attached to session, in this case all changes are persited when transaction is closed.

        return state;
    }

    @Override
    public IStatus getStatus() {
        return m_log.asMultiStatusOrOK(Messages.getString("CheckinStatePdbOperation.7")); //$NON-NLS-1$
    }

    private void uploadProfile(final Session session, final WaterBody waterBody, final State state,
            final IProfileFeature feature, final EventUploadProvider eventProvider) throws PdbConnectException {
        final IProfile profil = feature.getProfile();

        final CrossSection section = new CrossSection();

        /* db relevant data */
        section.setWaterBody(waterBody);
        section.setState(state);
        section.setCreationDate(state.getCreationDate());
        section.setEditingDate(state.getEditingDate());
        section.setEditingUser(state.getEditingUser());
        section.setMeasurementDate(state.getMeasurementDate());

        /* Data from profile */
        final BigDecimal station = getStation(feature);
        section.setStation(station);
        final String name = CheckinStateOperation.createCrossSectionName(profil.getName(), station);

        /* Check for uniqueness of profile name */
        if (m_checkSectionNames && !m_sectionNames.addUniqueName(name)) {
            final String message = String.format(Messages.getString("CheckinStatePdbOperation.6"), station, name); //$NON-NLS-1$
            throw new PdbConnectException(message);
        }

        section.setName(name);
        section.setDescription(profil.getComment());

        final String srsName = feature.getSrsName();
        createParts(session, state, section, profil, srsName, eventProvider);

        saveSection(session, state, section);

        final CheckinDocumentWorker worker = new CheckinDocumentWorker(m_data.getDocumentBase());
        worker.createDocuments(session, section, feature);
    }

    private void saveSection(final Session session, final State state, final CrossSection section) {
        if (session == null) {
            /* HINT: We use the state as object to store only the cross sections, if no session is available. */
            state.getCrossSections().add(section);
            return;
        }

        session.save(section);

        final Set<CrossSectionPart> parts = section.getCrossSectionParts();
        for (final CrossSectionPart part : parts) {
            session.save(part);

            final Set<Point> points = part.getPoints();
            for (final Point point : points)
                session.save(point);

            final Set<CrossSectionPartParameter> parameters = part.getCrossSectionPartParameters();
            for (final CrossSectionPartParameter parameter : parameters)
                session.save(parameter);
        }
    }

    private BigDecimal getStation(final IProfileFeature feature) {
        final BigDecimal station = feature.getBigStation();
        if (station == null)
            return null;

        return station.movePointRight(3);
    }

    private void createParts(final Session session, final State state, final CrossSection section,
            final IProfile profile, final String profilSRS, final EventUploadProvider eventProvider)
            throws PdbConnectException {
        final Set<CrossSectionPart> parts = new HashSet<>();

        /* Extract profile line. */
        final CrossSectionPart pPart = builtPart(profile, profilSRS);
        if (!isBlank(pPart)) {
            parts.add(pPart);
            section.setLine(pPart.getLine());
        }

        /* Extract extra parts. */
        final CrossSectionPart[] additionalParts = createAdditionalParts(session, state, profile, eventProvider);
        for (final CrossSectionPart additionalPart : additionalParts)
            parts.add(additionalPart);

        /* Generate unique names. */
        final PDBNameGenerator partNameGenerator = new PDBNameGenerator();
        for (final CrossSectionPart part : parts) {
            /* Instead of preserving the existing part name, we recreate it by the same system as when importing gaf */
            // REMARK: null happens, if we export to gaf format
            final CrossSectionPartType partType = part.getCrossSectionPartType();
            if (partType != null) {
                final String category = partType.getCategory();

                final String uniquePartName = partNameGenerator.createUniqueName(category);
                part.setName(uniquePartName);
            }

            part.setCrossSection(section);
            section.getCrossSectionParts().add(part);
        }
    }

    private boolean isBlank(final CrossSectionPart part) {
        if (part == null)
            return true;

        return part.getPoints().isEmpty();
    }

    /**
     * This function creates a cross section part using the records of the profile.
     * 
     * @param profile
     *          The profile.
     * @param profileSRS
     *          The coordinate system of the profile.
     * @return The cross section part.
     */
    private CrossSectionPart builtPart(final IProfile profile, final String profileSRS) throws PdbConnectException {
        final CheckinPartOperation partOperation = new CheckinPartOperation(m_data, profile, profileSRS);
        final IStatus result = partOperation.execute();
        if (!result.isOK())
            m_log.add(result);

        final CrossSectionPartType type = m_data.findPartType(GafKind.P.toString());
        final CrossSectionPart part = partOperation.getPart();
        part.setCrossSectionPartType(type);

        return part;
    }

    private CrossSectionPart[] createAdditionalParts(final Session session, final State state,
            final IProfile profile, final EventUploadProvider eventProvider) throws PdbConnectException {
        /* Memory for the cloned profile objects. */
        final List<IProfileObject> clonedProfileObjects = new ArrayList<>();

        /* Clone profile objects. */
        final IProfileObject[] profileObjects = profile.getProfileObjects();
        for (final IProfileObject profileObject : profileObjects) {
            final IProfileObject clonedProfileObject = cloneProfileObject(profileObject);
            clonedProfileObjects.add(clonedProfileObject);
        }

        /* Repair. */
        repairBridges(clonedProfileObjects);

        /* Update from components of profile. */
        updateFromComponents(clonedProfileObjects, profile);

        /* Remove empty OKs. */
        removeEmptyOks(clonedProfileObjects);

        /* Checkin the profile objects. */
        return checkin(session, state, clonedProfileObjects, profile, eventProvider);
    }

    private IProfileObject cloneProfileObject(final IProfileObject profileObject) {
        if (profileObject == null)
            return null;

        return ProfileObjectHelper.cloneProfileObject(profileObject, null);
    }

    private void repairBridges(final List<IProfileObject> clonedProfileObjects) {
        /* Iterate over the array, because we may change the list. */
        final IProfileObject[] clonedProfileObjectsArray = clonedProfileObjects.toArray(new IProfileObject[] {});

        /* The used ids. */
        final Set<String> usedIds = BridgeIdHelper.findUsedIds(clonedProfileObjectsArray);

        /* Check each profile object. */
        for (final IProfileObject clonedProfileObject : clonedProfileObjectsArray) {
            /* Repair bridges. */
            if (clonedProfileObject instanceof BuildingBruecke) {
                /* Cast. */
                final BuildingBruecke bridge = (BuildingBruecke) clonedProfileObject;

                /* If a ok profile object was found, both, bridge and ok profile object have an id. */
                final IProfileObject okProfileObject = BuildingBruecke.findOkProfileObject(bridge,
                        clonedProfileObjectsArray);
                if (okProfileObject != null)
                    continue;

                /* We create/reuse the bridge id. */
                final String bridgeId = BridgeIdHelper.findFreeId(bridge, usedIds);
                bridge.setBrueckeId(bridgeId);

                /* Search for ok profile object without bridge id and use it. */
                final IProfileObject okPO = BridgeIdHelper.findOkProfileObjectWithoutId(clonedProfileObjectsArray);
                if (okPO != null) {
                    okPO.setValue(BuildingBruecke.KEY_BRUECKE_ID, bridgeId);
                    continue;
                }

                /* no ok part found, create it on the fly */
                final GenericProfileHorizon genericProfileHorizon = new GenericProfileHorizon(
                        BuildingBruecke.ID_OK);
                genericProfileHorizon.setValue(BuildingBruecke.KEY_BRUECKE_ID, bridgeId);

                clonedProfileObjects.add(genericProfileHorizon);
            }
        }
    }

    private void updateFromComponents(final List<IProfileObject> clonedProfileObjects, final IProfile profile) {
        for (final IProfileObject clonedProfileObject : clonedProfileObjects) {
            if (clonedProfileObject instanceof BuildingBruecke)
                ProfileObjectHelper.updateObjectFromComponents(profile, clonedProfileObject,
                        IWspmTuhhConstants.POINT_PROPERTY_UNTERKANTEBRUECKE);

            if (clonedProfileObject instanceof GenericProfileHorizon) {
                if (clonedProfileObject.getType().equals(BuildingBruecke.ID_OK))
                    ProfileObjectHelper.updateObjectFromComponents(profile, clonedProfileObject,
                            IWspmTuhhConstants.POINT_PROPERTY_OBERKANTEBRUECKE);
            }

            if (clonedProfileObject instanceof BuildingWehr)
                ProfileObjectHelper.updateObjectFromComponents(profile, clonedProfileObject,
                        IWspmTuhhConstants.POINT_PROPERTY_OBERKANTEWEHR);

            if (m_updateCulvertObjects) {
                if (clonedProfileObject instanceof BuildingEi)
                    ProfileObjectHelper.updateObjectFromMetadata(profile, (BuildingEi) clonedProfileObject,
                            ((BuildingEi) clonedProfileObject).getHoehe(), GafPointCode.EIFS.getKey(),
                            GafPointCode.EIUK.getKey());

                if (clonedProfileObject instanceof BuildingKreis)
                    ProfileObjectHelper.updateObjectFromMetadata(profile, (BuildingKreis) clonedProfileObject, null,
                            GafPointCode.KRFS.getKey(), GafPointCode.KRUK.getKey());

                if (clonedProfileObject instanceof BuildingMaul)
                    ProfileObjectHelper.updateObjectFromMetadata(profile, (BuildingMaul) clonedProfileObject,
                            ((BuildingMaul) clonedProfileObject).getHoehe(), GafPointCode.MAFS.getKey(),
                            GafPointCode.MAUK.getKey());
            }
        }
    }

    private void removeEmptyOks(final List<IProfileObject> clonedProfileObjects) {
        /* Iterate over the array, because we may change the list. */
        final IProfileObject[] clonedProfileObjectsArray = clonedProfileObjects.toArray(new IProfileObject[] {});
        for (final IProfileObject clonedProfileObject : clonedProfileObjectsArray) {
            if (!(clonedProfileObject instanceof GenericProfileHorizon))
                continue;

            if (BuildingBruecke.ID_OK.equals(clonedProfileObject.getType())) {
                final IProfileObjectRecords records = clonedProfileObject.getRecords();
                if (records.size() == 0)
                    clonedProfileObjects.remove(clonedProfileObject);
            }
        }
    }

    private CrossSectionPart[] checkin(final Session session, final State state,
            final List<IProfileObject> clonedProfileObjects, final IProfile profile,
            final EventUploadProvider eventProvider) throws PdbConnectException {
        final List<CrossSectionPart> parts = new ArrayList<>();

        final CrossSectionPartTypes partTypes = session == null ? null : new CrossSectionPartTypes(session);

        final double station = profile.getStation();

        final int dbSRID = m_data.getGeometryFactory().getSRID();

        final int profileSRID = JTSAdapter.toSrid(profile.getSrsName());

        final ICoefficients coefficients = m_data.getCoefficients();

        for (final IProfileObject clonedProfileObject : clonedProfileObjects) {
            final Pair<Event, Boolean> findEventResult = findEvent(state, clonedProfileObject, eventProvider);

            final boolean shouldUpload = findEventResult.getValue();
            if (shouldUpload) {
                final Event event = findEventResult.getKey();
                final CheckinHorizonPartOperation operation = new CheckinHorizonPartOperation(clonedProfileObject,
                        profileSRID, dbSRID, station, partTypes, event, coefficients);
                operation.execute();

                final CrossSectionPart part = operation.getPart();
                parts.add(part);
            } else {
                // TODO: part ws not uploaded -> log it!
                final String eventName = clonedProfileObject.getValue(IWspmTuhhConstants.PROFIL_PROPERTY_EVENT_NAME,
                        null);
                System.out.format(Messages.getString("CheckinStatePdbOperation.8"), profile.getStation(), //$NON-NLS-1$
                        clonedProfileObject.getType(), eventName);
            }
        }

        return parts.toArray(new CrossSectionPart[] {});
    }

    /**
     * Determine the event for the given profile object
     */
    private Pair<Event, Boolean> findEvent(final State state, final IProfileObject profileObject,
            final EventUploadProvider eventProvider) {
        // REMARK: no event provider present if we export gaf, in this case, export everything that is really GAF
        if (eventProvider == null) {
            final String type = profileObject.getType();
            final String kindName = new GafPartsMapping().partType2kindName(type);

            final boolean shouldUpload = GafKind.quietValueOf(kindName) != null;
            return Pair.of(null, shouldUpload);
        }

        final String eventName = profileObject.getValue(IWspmTuhhConstants.PROFIL_PROPERTY_EVENT_NAME, null);

        /* if noevent name is set, we do upload the part; no event to be attached */
        if (StringUtils.isBlank(eventName))
            return Pair.of(null, true);

        final WaterBody waterBody = m_data.getWaterBody();

        /* try to find the event with this name in the database */
        final Event event = eventProvider.getEvent(waterBody, state, eventName);

        /* if event is not found, do not upload waterlevel */
        return Pair.of(event, event != null);
    }
}