org.jabylon.rest.ui.wicket.panels.PropertyListPanel.java Source code

Java tutorial

Introduction

Here is the source code for org.jabylon.rest.ui.wicket.panels.PropertyListPanel.java

Source

/**
 * (C) Copyright 2013 Jabylon (http://www.jabylon.org) and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.jabylon.rest.ui.wicket.panels;

import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

import javax.inject.Inject;
import javax.servlet.http.HttpServletResponse;

import org.apache.wicket.AttributeModifier;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.Session;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.extensions.markup.html.repeater.data.table.filter.IFilterStateLocator;
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.StatelessForm;
import org.apache.wicket.markup.html.form.upload.FileUpload;
import org.apache.wicket.markup.html.form.upload.FileUploadField;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.link.StatelessLink;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.request.http.flow.AbortWithHttpErrorCodeException;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.cdo.util.CommitException;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.jabylon.cdo.connector.Modification;
import org.jabylon.cdo.connector.TransactionUtil;
import org.jabylon.common.resolver.URIResolver;
import org.jabylon.common.review.ReviewParticipant;
import org.jabylon.common.util.URLUtil;
import org.jabylon.properties.Comment;
import org.jabylon.properties.Project;
import org.jabylon.properties.ProjectLocale;
import org.jabylon.properties.ProjectVersion;
import org.jabylon.properties.PropertiesFactory;
import org.jabylon.properties.Property;
import org.jabylon.properties.PropertyFile;
import org.jabylon.properties.PropertyFileDescriptor;
import org.jabylon.properties.Review;
import org.jabylon.properties.ReviewState;
import org.jabylon.properties.Severity;
import org.jabylon.properties.util.PropertyResourceUtil;
import org.jabylon.resources.persistence.PropertyPersistenceService;
import org.jabylon.rest.ui.Activator;
import org.jabylon.rest.ui.model.EClassSortState;
import org.jabylon.rest.ui.model.EObjectModel;
import org.jabylon.rest.ui.model.PropertyPair;
import org.jabylon.rest.ui.security.CDOAuthenticatedSession;
import org.jabylon.rest.ui.security.RestrictedComponent;
import org.jabylon.rest.ui.util.WicketUtil;
import org.jabylon.rest.ui.wicket.BasicResolvablePanel;
import org.jabylon.rest.ui.wicket.components.ConfirmBehaviour;
import org.jabylon.rest.ui.wicket.pages.ResourcePage;
import org.jabylon.security.CommonPermissions;
import org.jabylon.users.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

public class PropertyListPanel extends BasicResolvablePanel<PropertyFileDescriptor> implements RestrictedComponent {

    private static final long serialVersionUID = 1L;
    IModel<Multimap<String, Review>> reviewModel;
    static final String OK_LABEL = "OK";

    private static final Logger logger = LoggerFactory.getLogger(PropertyPairListDataProvider.class);

    @Inject
    List<ReviewParticipant> reviewParticipants;

    @Inject
    private PropertyPersistenceService propertyPersistence;
    private PropertyListMode mode;

    public PropertyListPanel(PropertyFileDescriptor object, PageParameters parameters) {
        super("content", object, parameters);
    }

    @Override
    protected void construct() {
        super.construct();

        List<ReviewParticipant> activeReviews = PropertyListModeFactory.filterActiveReviews(
                getModel().getObject().getProjectLocale().getParent().getParent(), reviewParticipants);
        mode = PropertyListModeFactory.allAsMap(activeReviews).get(getPageParameters().get("mode").toString());
        addLinkList(activeReviews, mode);
        reviewModel = new LoadableDetachableModel<Multimap<String, Review>>() {

            private static final long serialVersionUID = 1L;

            @Override
            protected Multimap<String, Review> load() {
                return buildReviewMap(getModelObject());
            }
        };
        PropertyPairListDataProvider provider = new PropertyPairListDataProvider(getModelObject(), mode,
                reviewModel);
        List<PropertyPair> contents = provider.createContents();

        ListView<PropertyPair> properties = new ListView<PropertyPair>("repeater", contents) {

            private static final long serialVersionUID = -7087485011138279358L;

            @Override
            protected void populateItem(final ListItem<PropertyPair> item) {
                IModel<PropertyPair> model = item.getModel();
                String key = model.getObject().getKey();
                Collection<Review> reviewList = reviewModel.getObject().get(key);
                BookmarkablePageLink<Object> link = new BookmarkablePageLink<Object>("edit", getPage().getClass(),
                        new PageParameters(getPageParameters()).add("key", key));
                item.add(link);
                link.setMarkupId(URLUtil.escapeToIdAttribute(key));
                link.setOutputMarkupId(true);
                link.add(new AttributeModifier("name", link.getMarkupId()));
                Label keyLabel = new Label("key", key);
                keyLabel.add(new AttributeModifier("title", model.getObject().getTranslated()));
                item.add(keyLabel);

                Label translationLabel = new Label("translation", model.getObject().getTranslated());
                translationLabel.add(new AttributeModifier("title", model.getObject().getTranslatedComment()));
                item.add(translationLabel);
                fillStatusColumn(model.getObject(), reviewList, item);
            }

        };
        properties.setOutputMarkupId(true);
        add(properties);

        String contextPath = WicketUtil.getContextPath();
        String href = contextPath + "/api/" + getModelObject().toURI().appendQuery("type=file");
        ExternalLink link = new ExternalLink("download.link", href);
        File file = new File(getModelObject().absoluteFilePath().toFileString());
        boolean enabled = file.isFile();
        link.setEnabled(enabled);
        if (!enabled) {
            link.add(new AttributeAppender("disabled", Model.of("disabled")));
            link.add(new AttributeAppender("class", Model.of("disabled")));
        }
        add(link);

        StatelessLink<Void> deleteLink = new DeleteLink("remove.button");
        deleteLink.add(new ConfirmBehaviour(nls("confirm.remove")));
        deleteLink.setVisible(canConfigure());
        add(deleteLink);

        FileUploadForm uploadForm = new FileUploadForm("translation-upload-form", getModel());
        uploadForm.setVisible(canSuggest());
        add(uploadForm);
    }

    @Override
    public String getRequiredPermission() {
        Project project = getModel().getObject().getProjectLocale().getParent().getParent();
        return CommonPermissions.constructPermission(CommonPermissions.PROJECT, project.getName(),
                CommonPermissions.ACTION_VIEW);
    }

    protected void fillStatusColumn(PropertyPair propertyPair, Collection<Review> reviewCollection,
            MarkupContainer container) {
        IStatus status = calculateRowStatus(propertyPair);
        if (status.getSeverity() == IStatus.WARNING)
            container.add(new AttributeModifier("class", "warning"));
        else if (status.getSeverity() == IStatus.ERROR)
            container.add(new AttributeModifier("class", "error"));

        Collection<Review> reviews = reviewCollection;
        if (reviews == null || reviews.isEmpty())
            reviews = createInMemoryReview(propertyPair);

        RepeatingView view = new RepeatingView("reviews");
        DateFormat formatter = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT,
                getSession().getLocale());
        for (Review review : reviews) {
            if (review.getState() == ReviewState.INVALID || review.getState() == ReviewState.RESOLVED)
                continue;
            Label label = new Label(view.newChildId(), review.getReviewType());
            label.add(new AttributeAppender("class", getLabelClass(review)));
            StringBuilder title = new StringBuilder();
            if (review.getMessage() != null)
                title.append(review.getMessage());
            if (review.getCreated() > 0) {
                if (title.length() > 0)
                    //add a linebreak
                    title.append("\n");
                title.append(formatter.format(new Date(review.getCreated())));
            }
            if (title.length() > 0)
                label.add(new AttributeModifier("title", title.toString()));
            view.add(label);
        }

        container.add(view);

    }

    private IStatus calculateRowStatus(PropertyPair propertyPair) {

        if (propertyPair.getOriginal() == null || propertyPair.getOriginal().isEmpty())
            return new Status(IStatus.ERROR, "org.jabylon.rest.ui", "");
        else if (propertyPair.getTranslated() == null || propertyPair.getTranslated().isEmpty())
            return new Status(IStatus.ERROR, "org.jabylon.rest.ui", "");
        return Status.OK_STATUS;
    }

    private List<Review> createInMemoryReview(PropertyPair pair) {
        if (pair.getTranslated() == null || pair.getTranslated().isEmpty()) {
            Review review = PropertiesFactory.eINSTANCE.createReview();
            String message = new StringResourceModel("review.missing.translation", this, null, pair.getKey())
                    .getString();
            review.setMessage(MessageFormat.format(message, pair.getKey()));
            review.setReviewType(getString("review.missing.translation.type"));
            review.setSeverity(Severity.ERROR);
            return Collections.singletonList(review);
        }
        Review review = PropertiesFactory.eINSTANCE.createReview();
        review.setReviewType("OK");
        return Collections.singletonList(review);
    }

    protected String getLabelClass(Review review) {
        if (OK_LABEL.equals(review.getReviewType()))
            return " label-success";

        Severity severity = review.getSeverity();
        switch (severity) {
        case ERROR:
            return " label-important";
        case INFO:
            return " label-info";
        case WARNING:
            return " label-warning";
        default:
            return "";
        }
    }

    /**
     * checks if the user has permissions to configure this project
     * @return
     */
    private boolean canConfigure() {
        return hasPermission(getModel().getObject(), getSession(), CommonPermissions.ACTION_CONFIG);
    }

    /**
      * checks if the user has permissions to make suggestions to this project
      * @return
      */
    private boolean canSuggest() {
        return hasPermission(getModel().getObject(), getSession(), CommonPermissions.ACTION_SUGGEST);
    }

    private static boolean hasPermission(PropertyFileDescriptor model, Session session, String permission) {
        ProjectVersion version = model.getProjectLocale().getParent();
        if (version.isReadOnly())
            return false;
        if (session instanceof CDOAuthenticatedSession) {
            Project project = version.getParent();
            CDOAuthenticatedSession authSession = (CDOAuthenticatedSession) session;
            return authSession.hasPermission(CommonPermissions.constructPermission(CommonPermissions.PROJECT,
                    project.getName(), permission));
        }
        return false;
    }

    private Multimap<String, Review> buildReviewMap(PropertyFileDescriptor object) {
        EList<Review> reviews = object.getReviews();
        Multimap<String, Review> reviewMap = ArrayListMultimap.create(reviews.size(), 2);
        for (Review review : reviews) {
            if (review.getState() == ReviewState.OPEN || review.getState() == ReviewState.REOPENED)
                reviewMap.put(review.getKey(), review);
        }
        return reviewMap;
    }

    private void addLinkList(List<ReviewParticipant> activeReviews, final PropertyListMode currentMode) {
        List<PropertyListMode> values = PropertyListModeFactory.all(activeReviews);
        ListView<PropertyListMode> mode = new ListView<PropertyListMode>("view-mode", values) {

            private static final long serialVersionUID = 1L;

            @Override
            protected void populateItem(ListItem<PropertyListMode> item) {
                String mode = item.getModelObject().name();
                BookmarkablePageLink<Object> link = new BookmarkablePageLink<Object>("link", getPage().getClass(),
                        new PageParameters(getPageParameters()).set("mode", mode));
                ReviewParticipant participant = item.getModel().getObject().getParticipant();
                if (participant != null)
                    link.setBody(nls(participant.getClass(), participant.getName()));
                else
                    link.setBody(new StringResourceModel(item.getModelObject().name(), item, null));
                item.add(link);
                if (item.getModelObject().equals(currentMode))
                    item.add(new AttributeModifier("class", "active"));
            }
        };
        add(mode);

    }

    private static class FileUploadForm extends StatelessForm<PropertyFileDescriptor> {

        private static final long serialVersionUID = -3653084217384164795L;
        private static final Logger LOG = LoggerFactory.getLogger(FileUploadForm.class);

        private final ArrayList<FileUpload> uploads;
        private FileUploadField fileUploadField;

        public FileUploadForm(String id, IModel<PropertyFileDescriptor> model) {
            super(id, model);
            // set this form to multipart mode (always needed for uploads!)
            setMultiPart(true);
            uploads = new ArrayList<FileUpload>();

            // Add one multi-file upload field
            @SuppressWarnings({ "unchecked", "rawtypes" })
            //            IModel<List<FileUpload>> fileModel = new Model<ArrayList<FileUpload>>(uploads);
            IModel<List<FileUpload>> fileModel = new Model(uploads);
            fileUploadField = new FileUploadField("upload", fileModel);
            add(fileUploadField);

            // Set maximum size to 100K for demo purposes
            //            setMaxSize(Bytes.kilobytes(100));
        }

        @Override
        protected void onSubmit() {
            super.onSubmit();

            for (FileUpload fileUpload : fileUploadField.getFileUploads()) {
                PropertyFileDescriptor descriptor = getModel().getObject();
                try {
                    PropertyFile file = descriptor.loadProperties(fileUpload.getInputStream());
                    persist(file);

                } catch (IOException e) {
                    LOG.error("Failed to retrieve uploaded file for" + descriptor.getLocation(), e);
                }
            }
        }

        private void persist(PropertyFile file) {
            PropertyPersistenceService persistenceService = Activator.getDefault().getPersistenceService();
            try {
                PropertyFile original = persistenceService.loadProperties(getModelObject());
                PropertyFile master = persistenceService.loadProperties(getModelObject().getMaster());
                Map<String, Property> originalProperties = original.asMap();

                for (Property prop : master.getProperties()) {
                    if (!originalProperties.containsKey(prop.getKey())) {
                        Property newProperty = PropertiesFactory.eINSTANCE.createProperty();
                        newProperty.setKey(prop.getKey());
                        original.getProperties().add(newProperty);
                    }
                }
                originalProperties = original.asMap();
                EList<Property> newProperties = file.getProperties();
                if (hasPermission(getModelObject(), getSession(), CommonPermissions.ACTION_EDIT)) {
                    boolean hadChanges = false;
                    for (Property newProp : newProperties) {
                        Property origProp = originalProperties.get(newProp.getKey());
                        if (origProp == null)
                            continue;
                        if (newProp.getValue() != null && !newProp.getValue().isEmpty()
                                && !newProp.getValue().equals(origProp.getValue())) {
                            hadChanges = true;
                            origProp.setValue(newProp.getValue());
                            origProp.setComment(newProp.getComment());
                        }
                    }
                    if (hadChanges)
                        persistenceService.saveProperties(getModelObject(), original);
                } else if (hasPermission(getModelObject(), getSession(), CommonPermissions.ACTION_SUGGEST)) {
                    final List<Review> reviews = new ArrayList<Review>();
                    for (Property newProp : newProperties) {
                        Property origProp = originalProperties.get(newProp.getKey());
                        if (origProp == null)
                            continue;
                        if (newProp.getValue() != null && !newProp.getValue().isEmpty()
                                && !newProp.getValue().equals(origProp.getValue())) {

                            final Review review = PropertiesFactory.eINSTANCE.createReview();
                            review.setCreated(System.currentTimeMillis());
                            review.setKey(newProp.getKey());
                            review.setMessage(newProp.getValue());
                            review.setSeverity(Severity.INFO);
                            review.setReviewType(Review.KIND_SUGGESTION);

                            String username = CommonPermissions.USER_ANONYMOUS;
                            Session session = getSession();
                            if (session instanceof CDOAuthenticatedSession) {
                                CDOAuthenticatedSession authSession = (CDOAuthenticatedSession) session;
                                User user = authSession.getUser();
                                if (user != null)
                                    username = user.getName();

                            }
                            review.setUser(username);

                            if (newProp.getComment() != null && !newProp.getComment().isEmpty()) {
                                Comment comment = PropertiesFactory.eINSTANCE.createComment();
                                comment.setUser(username);
                                comment.setCreated(review.getCreated());
                                comment.setMessage(newProp.getComment());
                                review.getComments().add(comment);
                            }
                            reviews.add(review);
                        }
                    }
                    if (!reviews.isEmpty()) {

                        TransactionUtil.commit(getModelObject(),
                                new Modification<PropertyFileDescriptor, PropertyFileDescriptor>() {
                                    @Override
                                    public PropertyFileDescriptor apply(PropertyFileDescriptor object) {

                                        object.getReviews().addAll(reviews);
                                        return object;
                                    }

                                });

                    }
                }
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {

                }
                getSession().success(new StringResourceModel("message.upload.successfull", this, null).getString());
                setResponsePage(ResourcePage.class, getPage().getPageParameters());
            } catch (ExecutionException e) {
                LOG.error("Failed to merge uploaded properties with " + getModelObject().getLocation(), e);
                getSession().success(
                        new StringResourceModel("message.upload.failed", this, null, e.getMessage()).getString());
            } catch (CommitException e) {
                logger.error("Commit of suggestion failed", e);
                getSession().success(
                        new StringResourceModel("message.upload.failed", this, null, e.getMessage()).getString());
            }

        }

    }

    class PropertyPairListDataProvider extends SortableDataProvider<PropertyPair, EClassSortState>
            implements IFilterStateLocator<String> {

        private static final long serialVersionUID = 1L;
        private CompoundPropertyModel<PropertyFileDescriptor> model;
        private transient List<PropertyPair> contents;
        private String filterState;
        private PropertyListMode mode;
        private IModel<Multimap<String, Review>> reviewModel;

        public PropertyPairListDataProvider(PropertyFileDescriptor descriptor, PropertyListMode mode,
                IModel<Multimap<String, Review>> reviewModel) {
            super();
            model = new CompoundPropertyModel<PropertyFileDescriptor>(
                    new EObjectModel<PropertyFileDescriptor>(descriptor));
            this.mode = mode;
            this.reviewModel = reviewModel;
        }

        @Override
        public Iterator<? extends PropertyPair> iterator(long first, long count) {
            List<PropertyPair> contents = getList();
            return contents.subList((int) first, (int) first + (int) count).iterator();
        }

        private List<PropertyPair> getList() {
            if (contents == null) {
                contents = createContents();
            }
            return contents;
        }

        protected List<PropertyPair> createContents() {
            PropertyFileDescriptor descriptor = model.getObject();
            Multimap<String, Review> reviews = reviewModel.getObject();
            PropertyFileDescriptor master = descriptor.getMaster();
            Map<String, Property> translated = new HashMap<String, Property>(loadProperties(descriptor).asMap());
            PropertyFile templateFile = loadProperties(master);

            List<PropertyPair> contents = new ArrayList<PropertyPair>();
            for (Property property : templateFile.getProperties()) {
                // IModel<String> bind = model.bind(property.getKey());
                // bind.set
                PropertyPair pair = new PropertyPair(property, translated.remove(property.getKey()),
                        descriptor.getVariant(), descriptor.cdoID());
                String key = pair.getKey();
                if (mode.apply(pair, reviews.get(key)))
                    contents.add(pair);
            }
            for (Property property : translated.values()) {
                PropertyPair pair = new PropertyPair(null, property, descriptor.getVariant(), descriptor.cdoID());
                if (mode.apply(pair, reviews.get(pair.getKey())))
                    contents.add(pair);
            }
            return contents;
        }

        private PropertyFile loadProperties(PropertyFileDescriptor descriptor) {
            try {
                return propertyPersistence.loadProperties(descriptor);
            } catch (ExecutionException e) {
                logger.error("Failed to load properties for " + descriptor);
                throw new AbortWithHttpErrorCodeException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                        "Failed to load properties for " + descriptor);
            }
        }

        @Override
        public long size() {
            return getList().size();
        }

        @Override
        public IModel<PropertyPair> model(PropertyPair object) {
            return Model.of(object);
        }

        @Override
        public void setFilterState(String state) {
            this.filterState = state;

        }

        @Override
        public String getFilterState() {
            return filterState;
        }

    }

}

class DeleteLink extends StatelessLink<Void> {

    private static final long serialVersionUID = 8205155656605708520L;

    @Inject
    private URIResolver resolver;

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

    public DeleteLink(String id) {
        super(id);
    }

    @Override
    public void onClick() {
        PropertyFileDescriptor descriptor = getModel(getPage().getPageParameters());
        if (descriptor != null) {
            try {
                ProjectLocale locale = TransactionUtil.commit(descriptor,
                        new Modification<PropertyFileDescriptor, ProjectLocale>() {
                            @Override
                            public ProjectLocale apply(PropertyFileDescriptor object) {
                                ProjectLocale locale = object.getProjectLocale();
                                if (!object.isMaster())
                                    object = object.getMaster();
                                if (object != null)
                                    PropertyResourceUtil.removeDescriptor(object);
                                return locale;
                            };
                        });
                URI uri = resolver.getURI(locale);
                PageParameters params = new PageParameters();
                for (int i = 0; i < uri.segmentCount(); i++) {
                    params.set(i, uri.segment(i));
                }
                //go to the parent locale
                setResponsePage(ResourcePage.class, params);
            } catch (CommitException e) {
                error(e.getMessage());
                LOG.error("Failed to delete descriptor", e);
            }
        }

    }

    private PropertyFileDescriptor getModel(PageParameters pageParameters) {
        URI uri = URI.createURI("/");
        for (int i = 0; i < pageParameters.getIndexedCount(); i++) {
            uri = uri.appendSegment(pageParameters.get(i).toString());
        }

        return (PropertyFileDescriptor) resolver.resolve(uri);
    }
}