org.phenotips.entities.internal.AbstractPrimaryEntityGroup.java Source code

Java tutorial

Introduction

Here is the source code for org.phenotips.entities.internal.AbstractPrimaryEntityGroup.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/
 */
package org.phenotips.entities.internal;

import org.phenotips.components.ComponentManagerRegistry;
import org.phenotips.entities.PrimaryEntity;
import org.phenotips.entities.PrimaryEntityGroup;
import org.phenotips.entities.PrimaryEntityManager;

import org.xwiki.bridge.DocumentAccessBridge;
import org.xwiki.component.manager.ComponentLookupException;
import org.xwiki.component.manager.ComponentManager;
import org.xwiki.model.reference.EntityReference;
import org.xwiki.query.Query;
import org.xwiki.query.QueryException;
import org.xwiki.query.QueryManager;
import org.xwiki.stability.Unstable;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.BaseObject;

/**
 * Base class for implementing specific entity groups, where members declare the groups that they belong to.
 * <p>
 * By default, this uses the document name as the identifier, the document title as the name, and either a
 * {@code description} property in the main XObject, or the document content, as the description. If two objects use the
 * same document for storage, they are assumed to be equal, and no actual data equality is checked.
 * </p>
 * <p>
 * Members declare their group membership by attaching an XObject of type {@link #GROUP_MEMBERSHIP_CLASS} pointing to
 * the group document.
 * </p>
 * <p>
 * In order to function properly, a {@link PrimaryEntityManager} component with the hint ({@code @Named}) set to the
 * name of {@link #getMemberType() the XClass used for members} must exist. Shorter versions of the XClass name are also
 * accepted. For example, for members of type {@code PhenoTips.PatientClass}, the following names are supported:
 * </p>
 * <ol>
 * <li>PhenoTips.PatientClass</li>
 * <li>PatientClass</li>
 * <li>Patient</li>
 * </ol>
 *
 * @param <E> the type of entities belonging to this group; if more than one type of entities can be part of the group,
 *            then a generic {@code PrimaryEntity} should be used instead
 * @version $Id: 9cc6056242d436c8f50680587f090003c75a386e $
 * @since 1.3M2
 */
@Unstable("New class and interface added in 1.3")
public abstract class AbstractPrimaryEntityGroup<E extends PrimaryEntity> extends AbstractPrimaryEntity
        implements PrimaryEntityGroup<E> {
    protected final PrimaryEntityManager<E> membersManager;

    protected AbstractPrimaryEntityGroup(XWikiDocument document) {
        super(document);

        PrimaryEntityManager<E> manager = null;
        if (getMemberType() != null) {
            ComponentManager cm = ComponentManagerRegistry.getContextComponentManager();
            String[] possibleRoles = new String[3];
            possibleRoles[0] = getLocalSerializer().serialize(getMemberType());
            possibleRoles[1] = getMemberType().getName();
            possibleRoles[2] = StringUtils.removeEnd(getMemberType().getName(), "Class");
            for (String role : possibleRoles) {
                try {
                    manager = cm.getInstance(PrimaryEntityManager.class, role);
                } catch (ComponentLookupException ex) {
                    // TODO Use a generic manager that can work with any type of group
                }
            }
        }
        if (manager == null) {
            this.logger.error("No suitable primary entity manager found for entities of type [{}] available;"
                    + " certain group operations will fail", getMemberType());
        }
        this.membersManager = manager;
    }

    @Override
    public Collection<E> getMembers() {
        return getMembersOfType(getMemberType());
    }

    @Override
    public Collection<E> getMembersOfType(EntityReference type) {
        Collection<E> result = new LinkedList<>();
        try {
            StringBuilder hql = new StringBuilder();
            hql.append("select distinct binding.name from BaseObject binding, StringProperty groupReference");
            if (type != null) {
                hql.append(", BaseObject entity");
            }
            hql.append(" where binding.className = :memberClass").append(
                    " and groupReference.id.id = binding.id and groupReference.id.name = :referenceProperty")
                    .append(" and groupReference.value = :selfReference");
            if (type != null) {
                hql.append(" and entity.name = binding.name and entity.className = :entityType");
            }

            Query q = getQueryManager().createQuery(hql.toString(), Query.HQL);

            q.bindValue("memberClass", getLocalSerializer().serialize(getMembershipClass()));
            q.bindValue("referenceProperty", getMembershipProperty());
            q.bindValue("selfReference", getFullSerializer().serialize(getDocumentReference()));
            if (type != null) {
                q.bindValue("entityType", getLocalSerializer().serialize(type));
            }
            List<String> memberIds = q.execute();
            for (String memberId : memberIds) {
                result.add(this.membersManager.get(memberId));
            }
        } catch (QueryException ex) {
            this.logger.warn("Failed to query members: {}", ex.getMessage());
        }
        return result;
    }

    @Override
    public boolean addMember(E member) {
        try {
            DocumentAccessBridge dab = ComponentManagerRegistry.getContextComponentManager()
                    .getInstance(DocumentAccessBridge.class);
            XWikiDocument doc = (XWikiDocument) dab.getDocument(member.getDocumentReference());
            BaseObject obj = doc.getXObject(getMembershipClass(), getMembershipProperty(),
                    getFullSerializer().serialize(getDocumentReference()), false);
            if (obj != null) {
                return true;
            }
            obj = doc.newXObject(getMembershipClass(), getXContext());
            obj.setStringValue(getMembershipProperty(), getFullSerializer().serialize(getDocumentReference()));
            getXContext().getWiki().saveDocument(doc, "Added to group " + getDocumentReference(), true,
                    getXContext());
            return true;
        } catch (Exception ex) {
            this.logger.warn("Failed to add member to group: {}", ex.getMessage());
        }
        return false;
    }

    @Override
    public boolean removeMember(E member) {
        try {
            DocumentAccessBridge dab = ComponentManagerRegistry.getContextComponentManager()
                    .getInstance(DocumentAccessBridge.class);
            XWikiDocument doc = (XWikiDocument) dab.getDocument(member.getDocumentReference());
            BaseObject obj = doc.getXObject(getMembershipClass(), getMembershipProperty(),
                    getFullSerializer().serialize(getDocumentReference()), false);
            if (obj == null) {
                return true;
            }
            doc.removeXObject(obj);
            getXContext().getWiki().saveDocument(doc, "Removed from group " + getDocumentReference(), true,
                    getXContext());
            return true;
        } catch (Exception ex) {
            this.logger.warn("Failed to remove member from group: {}", ex.getMessage());
        }
        return false;
    }

    protected QueryManager getQueryManager() {
        try {
            return ComponentManagerRegistry.getContextComponentManager().getInstance(QueryManager.class);
        } catch (ComponentLookupException ex) {
            this.logger.error("Failed to access the query manager: {}", ex.getMessage(), ex);
        }
        return null;
    }

    protected EntityReference getMembershipClass() {
        return GROUP_MEMBERSHIP_CLASS;
    }

    protected String getMembershipProperty() {
        return REFERENCE_XPROPERTY;
    }
}