eu.dasish.annotation.backend.dao.impl.DBDispatcherImlp.java Source code

Java tutorial

Introduction

Here is the source code for eu.dasish.annotation.backend.dao.impl.DBDispatcherImlp.java

Source

/*
 * Copyright (C) 2013 DASISH
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package eu.dasish.annotation.backend.dao.impl;

import eu.dasish.annotation.backend.ForbiddenException;
import eu.dasish.annotation.backend.NotInDataBaseException;
import eu.dasish.annotation.backend.Resource;
import eu.dasish.annotation.backend.Helpers;
import eu.dasish.annotation.backend.MatchMode;
import eu.dasish.annotation.backend.PrincipalCannotBeDeleted;
import eu.dasish.annotation.backend.PrincipalExists;
import eu.dasish.annotation.backend.dao.AnnotationDao;
import eu.dasish.annotation.backend.dao.CachedRepresentationDao;
import eu.dasish.annotation.backend.dao.DBDispatcher;
import eu.dasish.annotation.backend.dao.NotebookDao;
import eu.dasish.annotation.backend.dao.ResourceDao;
import eu.dasish.annotation.backend.dao.TargetDao;
import eu.dasish.annotation.backend.dao.PrincipalDao;
import eu.dasish.annotation.schema.Action;
import eu.dasish.annotation.schema.ActionList;
import eu.dasish.annotation.schema.Annotation;
import eu.dasish.annotation.schema.AnnotationActionName;
import eu.dasish.annotation.schema.AnnotationBody;
import eu.dasish.annotation.schema.AnnotationInfo;
import eu.dasish.annotation.schema.AnnotationInfoList;
import eu.dasish.annotation.schema.CachedRepresentationFragment;
import eu.dasish.annotation.schema.CachedRepresentationFragmentList;
import eu.dasish.annotation.schema.CachedRepresentationInfo;
import eu.dasish.annotation.schema.Notebook;
import eu.dasish.annotation.schema.NotebookInfo;
import eu.dasish.annotation.schema.NotebookInfoList;
import eu.dasish.annotation.schema.TargetInfoList;
import eu.dasish.annotation.schema.Access;
import eu.dasish.annotation.schema.PermissionActionName;
import eu.dasish.annotation.schema.PermissionList;
import eu.dasish.annotation.schema.ReferenceList;
import eu.dasish.annotation.schema.ResponseBody;
import eu.dasish.annotation.schema.Target;
import eu.dasish.annotation.schema.TargetInfo;
import eu.dasish.annotation.schema.Principal;
import eu.dasish.annotation.schema.Permission;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.Number;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author olhsha
 */
public class DBDispatcherImlp implements DBDispatcher {

    @Autowired
    PrincipalDao principalDao;
    @Autowired
    CachedRepresentationDao cachedRepresentationDao;
    @Autowired
    TargetDao targetDao;
    @Autowired
    AnnotationDao annotationDao;
    @Autowired
    NotebookDao notebookDao;
    final static protected String admin = "admin";
    private static final Logger logger = LoggerFactory.getLogger(DBDispatcherImlp.class);

    //////////////////////////////////
    private ResourceDao getDao(Resource resource) {
        switch (resource) {
        case PRINCIPAL:
            return principalDao;
        case ANNOTATION:
            return annotationDao;
        case TARGET:
            return targetDao;
        case CACHED_REPRESENTATION:
            return cachedRepresentationDao;
        case NOTEBOOK:
            return notebookDao;
        default:
            return null;
        }
    }

    @Override
    public void setResourcesPaths(String relServiceURI) {
        principalDao.setResourcePath(relServiceURI + "/principals/");
        cachedRepresentationDao.setResourcePath(relServiceURI + "/cached/");
        targetDao.setResourcePath(relServiceURI + "/targets/");
        annotationDao.setResourcePath(relServiceURI + "/annotations/");
        notebookDao.setResourcePath(relServiceURI + "/notebooks/");
    }

    ///////////// GETTERS //////////////////////////
    @Override
    public Number getResourceInternalIdentifier(UUID externalID, Resource resource) throws NotInDataBaseException {
        return this.getDao(resource).getInternalID(externalID);
    }

    @Override
    public UUID getResourceExternalIdentifier(Number resourceID, Resource resource) {
        return this.getDao(resource).getExternalID(resourceID);
    }

    @Override
    public Annotation getAnnotation(Number annotationID) {
        Annotation result = annotationDao.getAnnotationWithoutTargetsAndPemissionList(annotationID);
        result.setOwnerHref(principalDao.getHrefFromInternalID(annotationDao.getOwner(annotationID)));
        List<Number> targetIDs = targetDao.getTargetIDs(annotationID);
        TargetInfoList sis = new TargetInfoList();
        for (Number targetID : targetIDs) {
            TargetInfo targetInfo = this.getTargetInfoFromTarget(targetDao.getTarget(targetID));
            sis.getTargetInfo().add(targetInfo);
        }
        result.setTargets(sis);
        this.fillInPermissionList(result.getPermissions().getPermission(), annotationID, Resource.ANNOTATION);
        return result;
    }

    @Override
    public Number getAnnotationOwnerID(Number annotationID) {
        return annotationDao.getOwner(annotationID);
    }

    @Override
    public Principal getAnnotationOwner(Number annotationID) {
        Number ownerID = annotationDao.getOwner(annotationID);
        return principalDao.getPrincipal(ownerID);
    }

    ///////////////////////////////////////////////////
    private void fillInPermissionList(List<Permission> listPermissions, Number resourceID, Resource resource) {
        List<Map<Number, String>> principalsAccesss = this.getDao(resource).getPermissions(resourceID);
        for (Map<Number, String> principalAccess : principalsAccesss) {
            Number[] principal = new Number[1];
            principalAccess.keySet().toArray(principal);
            Permission permission = new Permission();
            permission.setPrincipalHref(principalDao.getHrefFromInternalID(principal[0]));
            permission.setLevel(Access.fromValue(principalAccess.get(principal[0])));
            listPermissions.add(permission);
        }
    }

    @Override
    public PermissionList getPermissions(Number resourceID, Resource resource) {
        PermissionList result = new PermissionList();
        result.setPublic(this.getDao(resource).getPublicAttribute(resourceID));
        this.fillInPermissionList(result.getPermission(), resourceID, resource);
        return result;
    }

    ////////////////////////////////////////////////////////////////////////
    @Override
    public List<Number> getFilteredAnnotationIDs(UUID ownerId, String link, MatchMode matchMode, String text,
            Number inloggedPrincipalID, String accessMode, String namespace, String after, String before)
            throws NotInDataBaseException {

        Number ownerID;

        if (ownerId != null) {
            if (accessMode.equals("owner")) { // inloggedUser is the owner of the annotations
                if (!ownerId.equals(principalDao.getExternalID(inloggedPrincipalID))) {
                    logger.info(
                            "The inlogged principal is demanded to be the owner of the annotations, however the expected owner is different and has the UUID "
                                    + ownerId.toString());
                    return new ArrayList<Number>();
                } else {
                    ownerID = inloggedPrincipalID;
                }
            } else {
                ownerID = principalDao.getInternalID(ownerId);
            }

        } else {
            if (accessMode.equals("owner")) {
                ownerID = inloggedPrincipalID;
            } else {
                ownerID = null;
            }
        }

        //Filtering on the columns  of the annotation table 
        List<Number> annotationIDs = annotationDao.getFilteredAnnotationIDs(ownerID, text, namespace, after,
                before);

        // Filetring on accessMode, the junction table
        if (annotationIDs != null) {
            if (!annotationIDs.isEmpty()) {
                if (!accessMode.equals("owner")) {
                    Access access = Access.fromValue(accessMode);
                    if (access.equals(Access.NONE)) {
                        List<Number> annotationIDsfiletered = new ArrayList<Number>();
                        Access accessCurrent;
                        for (Number annotationID : annotationIDs) {
                            accessCurrent = annotationDao.getAccess(inloggedPrincipalID, annotationID);
                            if (accessCurrent.equals(Access.NONE)) {
                                annotationIDsfiletered.add(annotationID);
                            }
                        }
                        annotationIDs = annotationIDsfiletered; // yeaahhh I'm relying on garbage collector here                         
                    } else {
                        List<Number> annotationIDsAccess = annotationDao
                                .getAnnotationIDsPermissionAtLeast(inloggedPrincipalID, access);
                        List<Number> annotationIDsPublic = annotationDao.getAnnotationIDsPublicAtLeast(access);
                        List<Number> annotationIDsOwned = annotationDao
                                .getFilteredAnnotationIDs(inloggedPrincipalID, text, namespace, after, before);
                        int check1 = this.addAllNoRepetitions(annotationIDsAccess, annotationIDsPublic);
                        int check2 = this.addAllNoRepetitions(annotationIDsAccess, annotationIDsOwned);
                        annotationIDs.retainAll(annotationIDsAccess);// intersection
                    }
                }
            }

            // filtering on reference        
            return this.filterAnnotationIDsOnReference(annotationIDs, link, matchMode);
        }

        return annotationIDs;

    }

    /// helpers ///
    private List<Number> filterAnnotationIDsOnReference(List<Number> annotationIDs, String link,
            MatchMode matchMode) {
        if (link != null) {
            if (!link.isEmpty()) {
                if (annotationIDs != null) {
                    String partiallyEncoded = this.encodeURLNoSlashEncoded(link);
                    String urlEncoded = null;
                    try {
                        urlEncoded = URLEncoder.encode(link, "UTF-8");
                    } catch (UnsupportedEncodingException e) {
                        logger.debug(e.toString());
                    }

                    List<Number> result = new ArrayList();
                    for (Number annotationID : annotationIDs) {
                        List<Number> targets = targetDao.getTargetIDs(annotationID);
                        for (Number targetID : targets) {
                            if (!result.contains(annotationID)) {
                                String linkRunner = targetDao.getLink(targetID);
                                if (matchCriterium(linkRunner, link, matchMode)
                                        || matchCriterium(linkRunner, partiallyEncoded, matchMode)) {
                                    result.add(annotationID);
                                } else {
                                    if (urlEncoded != null) {
                                        if (matchCriterium(linkRunner, urlEncoded, matchMode)) {
                                            result.add(annotationID);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    return result;
                }
            }
        }
        return annotationIDs;
    }

    private boolean matchCriterium(String currentString, String pattern, MatchMode matchMode) {
        switch (matchMode) {
        case EXACT:
            return currentString.equals(pattern);
        case STARTS_WITH:
            return currentString.startsWith(pattern);
        case ENDS_WITH:
            return currentString.endsWith(pattern);
        case CONTAINS:
            return currentString.contains(pattern);
        default:
            return false;
        }
    }

    public int addAllNoRepetitions(List<Number> list, List<Number> listToAdd) {
        int result = 0;
        if (list != null) {
            if (listToAdd != null) {
                for (Number element : listToAdd) {
                    if (!list.contains(element)) {
                        list.add(element);
                        result++;
                    }
                }
            }
        } else {
            if (listToAdd != null) {
                list = listToAdd;
                result = listToAdd.size();
            }
        }
        return result;
    }

    private String encodeURLNoSlashEncoded(String string) {
        String[] split = string.split("/");
        StringBuilder result = new StringBuilder(split[0]);
        for (int i = 1; i < split.length; i++) {
            try {
                result.append("/").append(URLEncoder.encode(split[i], "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                result.append("/").append(split[i]);
                logger.debug(e.toString());
            }
        }
        return result.toString();
    }

    //////
    @Override
    public ReferenceList getAnnotationTargets(Number annotationID) {
        ReferenceList result = new ReferenceList();
        List<Number> targetIDs = targetDao.getTargetIDs(annotationID);
        for (Number targetID : targetIDs) {
            result.getHref().add(targetDao.getHrefFromInternalID(targetID));
        }
        return result;
    }

    @Override
    public List<String> getTargetsWithNoCachedRepresentation(Number annotationID) {

        List<String> result = new ArrayList<String>();
        List<Number> targetIDs = targetDao.getTargetIDs(annotationID);
        for (Number targetID : targetIDs) {
            List<Number> versions = cachedRepresentationDao.getCachedRepresentationsForTarget(targetID);
            if (versions.isEmpty()) {
                result.add(targetDao.getHrefFromInternalID(targetID));
            }
        }
        return result;
    }

    @Override
    public List<String> getPrincipalsWithNoInfo(Number annotationID) {
        List<String> result = new ArrayList<String>();
        List<Map<Number, String>> principalsWithAccesss = annotationDao.getPermissions(annotationID);
        for (Map<Number, String> permission : principalsWithAccesss) {
            Number[] principalID = new Number[1];
            permission.keySet().toArray(principalID);
            Principal principal = principalDao.getPrincipal(principalID[0]);
            if (principal.getDisplayName() == null || principal.getDisplayName().trim().isEmpty()
                    || principal.getEMail() == null || principal.getEMail().trim().isEmpty()) {
                result.add(principalDao.getHrefFromInternalID(principalID[0]));

            }
        }
        return result;
    }

    @Override
    public AnnotationInfoList getFilteredAnnotationInfos(UUID ownerId, String link, MatchMode matchMode,
            String text, Number inloggedPrincipalID, String access, String namespace, String after, String before)
            throws NotInDataBaseException {
        List<Number> annotationIDs = this.getFilteredAnnotationIDs(ownerId, link, matchMode, text,
                inloggedPrincipalID, access, namespace, after, before);
        AnnotationInfoList result = new AnnotationInfoList();
        for (Number annotationID : annotationIDs) {
            AnnotationInfo annotationInfo = annotationDao.getAnnotationInfoWithoutTargetsAndOwner(annotationID);
            annotationInfo.setTargets(this.getAnnotationTargets(annotationID));
            annotationInfo.setOwnerHref(principalDao.getHrefFromInternalID(annotationDao.getOwner(annotationID)));
            result.getAnnotationInfo().add(annotationInfo);
        }
        return result;
    }

    @Override
    public AnnotationInfoList getAllAnnotationInfos() {
        List<Number> annotationIDs = annotationDao.getAllAnnotationIDs();
        AnnotationInfoList result = new AnnotationInfoList();
        for (Number annotationID : annotationIDs) {
            Number ownerID = annotationDao.getOwner(annotationID);
            ReferenceList targets = this.getAnnotationTargets(annotationID);
            AnnotationInfo annotationInfo = annotationDao.getAnnotationInfoWithoutTargetsAndOwner(annotationID);
            annotationInfo.setTargets(targets);
            annotationInfo.setOwnerHref(principalDao.getHrefFromInternalID(ownerID));
            result.getAnnotationInfo().add(annotationInfo);
        }
        return result;

    }

    // TODO unit test
    @Override
    public Target getTarget(Number internalID) {
        Target result = targetDao.getTarget(internalID);
        result.setSiblingTargets(this.getTargetsForTheSameLinkAs(internalID));
        Map<Number, String> cachedIDsFragments = targetDao.getCachedRepresentationFragmentPairs(internalID);
        CachedRepresentationFragmentList cachedRepresentationFragmentList = new CachedRepresentationFragmentList();
        for (Number key : cachedIDsFragments.keySet()) {
            CachedRepresentationFragment cachedRepresentationFragment = new CachedRepresentationFragment();
            cachedRepresentationFragment.setHref(cachedRepresentationDao.getHrefFromInternalID(key));
            cachedRepresentationFragment.setFragmentString(cachedIDsFragments.get(key));
            cachedRepresentationFragmentList.getCached().add(cachedRepresentationFragment);
        }
        result.setCachedRepresentations(cachedRepresentationFragmentList);
        return result;
    }

    @Override
    public CachedRepresentationInfo getCachedRepresentationInfo(Number internalID) {
        return cachedRepresentationDao.getCachedRepresentationInfo(internalID);
    }

    @Override
    public InputStream getCachedRepresentationBlob(Number cachedID) {
        return cachedRepresentationDao.getCachedRepresentationBlob(cachedID);
    }

    @Override
    public ReferenceList getTargetsForTheSameLinkAs(Number targetID) {
        List<Number> targetIDs = targetDao.getTargetsForLink(targetDao.getLink(targetID));
        ReferenceList referenceList = new ReferenceList();
        for (Number siblingID : targetIDs) {
            if (!siblingID.equals(targetID)) {
                referenceList.getHref().add(targetDao.getHrefFromInternalID(siblingID));
            }
        }
        return referenceList;
    }

    @Override
    public Principal getPrincipal(Number principalID) {
        return principalDao.getPrincipal(principalID);
    }

    @Override
    public Principal getPrincipalByInfo(String eMail) throws NotInDataBaseException {
        return principalDao.getPrincipalByInfo(eMail);
    }

    @Override
    public String getPrincipalRemoteID(Number internalID) {
        return principalDao.getRemoteID(internalID);
    }

    @Override
    public Access getAccess(Number annotationID, Number principalID) {
        Access publicAttribute = annotationDao.getPublicAttribute(annotationID);
        Access access = annotationDao.getAccess(annotationID, principalID);
        if (publicAttribute.equals(Access.NONE)) {
            return access;
        } else {
            if (publicAttribute.equals(Access.READ)) {
                if (access.equals(Access.NONE)) {
                    return Access.READ;
                } else {
                    return access;
                }
            } else {
                if (publicAttribute.equals(Access.WRITE)) {
                    if (access.equals(Access.NONE) || access.equals(Access.READ)) {
                        return Access.WRITE;
                    } else {
                        return access;
                    }
                } else {
                    if (publicAttribute.equals(Access.ALL)) {
                        return Access.ALL;
                    } else {
                        logger.error(
                                "Database problem: the value of public attribute is not a proper Access value: "
                                        + publicAttribute.value());
                        return access;
                    }
                }
            }
        }
    }

    @Override
    public Access getPublicAttribute(Number annotationID) {
        return annotationDao.getPublicAttribute(annotationID);
    }

    @Override
    public Number getPrincipalInternalIDFromRemoteID(String remoteID) throws NotInDataBaseException {
        return principalDao.getPrincipalInternalIDFromRemoteID(remoteID);
    }

    @Override
    public UUID getPrincipalExternalIDFromRemoteID(String remoteID) throws NotInDataBaseException {
        return principalDao.getPrincipalExternalIDFromRemoteID(remoteID);
    }

    @Override
    public String getTypeOfPrincipalAccount(Number principalID) {
        return principalDao.getTypeOfPrincipalAccount(principalID);
    }

    @Override
    public Principal getDataBaseAdmin() {
        return principalDao.getPrincipal(principalDao.getDBAdminID());
    }

    // !!!so far implemented only for annotations!!!
    @Override
    public boolean canDo(Access action, Number principalID, Number resourceID, Resource resource) {

        switch (resource) {
        case ANNOTATION: {
            if (principalID.equals(annotationDao.getOwner(resourceID))
                    || principalDao.getTypeOfPrincipalAccount(principalID).equals(admin)) {
                return true;
            }
            Access access = this.getAccess(resourceID, principalID);
            return this.greaterOrEqual(access, action);
        }
        case CACHED_REPRESENTATION: {
            return true;
        }
        case TARGET: {
            return true;
        }
        case PRINCIPAL: {
            return true;
        }
        default:
            return false;
        }

    }

    private boolean greaterOrEqual(Access access, Access action) {

        if (access.equals(Access.ALL)) {
            return true;
        }

        if (access.equals(Access.WRITE) && (action.equals(Access.READ) || action.equals(Access.WRITE))) {
            return true;
        }
        if (access.equals(Access.READ) && action.equals(Access.READ)) {
            return true;
        }
        return false;
    }
    ////// noetbooks ///////
    /// TODO update for having attribute public!!! /////

    @Override
    public NotebookInfoList getNotebooks(Number principalID, Access access) {
        NotebookInfoList result = new NotebookInfoList();
        if (access.equals(Access.READ) || access.equals(Access.WRITE) || access.equals(Access.ALL)) {
            List<Number> notebookIDs = notebookDao.getNotebookIDs(principalID, access);
            for (Number notebookID : notebookIDs) {
                NotebookInfo notebookInfo = notebookDao.getNotebookInfoWithoutOwner(notebookID);
                Number ownerID = notebookDao.getOwner(notebookID);
                notebookInfo.setOwnerHref(principalDao.getHrefFromInternalID(ownerID));
                result.getNotebookInfo().add(notebookInfo);
            }
        }
        return result;
    }

    @Override
    public boolean hasAccess(Number notebookID, Number principalID, Access access) {
        List<Number> notebookIDs = notebookDao.getNotebookIDs(principalID, access);
        return notebookIDs.contains(notebookID);
    }

    @Override
    public ReferenceList getNotebooksOwnedBy(Number principalID) {
        ReferenceList result = new ReferenceList();
        List<Number> notebookIDs = notebookDao.getNotebookIDsOwnedBy(principalID);
        for (Number notebookID : notebookIDs) {
            String reference = notebookDao.getHrefFromInternalID(notebookID);
            result.getHref().add(reference);
        }
        return result;
    }

    @Override
    public ReferenceList getPrincipals(Number notebookID, String access) {
        ReferenceList result = new ReferenceList();
        List<Number> principalIDs = principalDao.getPrincipalIDsWithAccessForNotebook(notebookID,
                Access.fromValue(access));
        for (Number principalID : principalIDs) {
            String reference = principalDao.getHrefFromInternalID(principalID);
            result.getHref().add(reference);
        }
        return result;
    }

    @Override
    public Notebook getNotebook(Number notebookID) {
        Notebook result = notebookDao.getNotebookWithoutAnnotationsAndAccesssAndOwner(notebookID);

        result.setOwnerRef(principalDao.getHrefFromInternalID(notebookDao.getOwner(notebookID)));

        ReferenceList annotations = new ReferenceList();
        List<Number> annotationIDs = annotationDao.getAnnotations(notebookID);
        for (Number annotationID : annotationIDs) {
            annotations.getHref().add(annotationDao.getHrefFromInternalID(annotationID));
        }
        result.setAnnotations(annotations);

        PermissionList ups = new PermissionList();
        List<Access> accesss = new ArrayList<Access>();
        accesss.add(Access.READ);
        accesss.add(Access.WRITE);
        accesss.add(Access.ALL);
        for (Access access : accesss) {
            List<Number> principals = principalDao.getPrincipalIDsWithAccessForNotebook(notebookID, access);
            if (principals != null) {
                for (Number principal : principals) {
                    Permission up = new Permission();
                    up.setPrincipalHref(principalDao.getHrefFromInternalID(principal));
                    up.setLevel(access);
                    ups.getPermission().add(up);
                }
            }
        }
        result.setPermissions(ups);
        return result;
    }

    @Override
    public Number getNotebookOwner(Number notebookID) {
        return notebookDao.getOwner(notebookID);
    }

    /////////////////////////////////////////////////////////////

    @Override
    public ReferenceList getAnnotationsForNotebook(Number notebookID, int startAnnotation, int maximumAnnotations,
            String orderedBy, boolean desc) {
        List<Number> annotationIDs = annotationDao.getAnnotations(notebookID);

        if (startAnnotation < 0) {
            logger.info("Variable's startAnnotation value " + startAnnotation + " is invalid. I will return null.");
            return null;
        }

        if (maximumAnnotations < -1) {
            logger.info("Variable's maximumAnnotations value " + maximumAnnotations
                    + " is invalid. I will return null.");
            return null;
        }

        int offset = startAnnotation - 1;
        String direction = desc ? "DESC" : "ASC";
        List<Number> selectedAnnotIDs = annotationDao.sublistOrderedAnnotationIDs(annotationIDs, offset,
                maximumAnnotations, orderedBy, direction);
        ReferenceList references = new ReferenceList();
        for (Number annotationID : selectedAnnotIDs) {
            references.getHref().add(annotationDao.getHrefFromInternalID(annotationID));
        }
        return references;
    }

    ///// UPDATERS /////////////////
    @Override
    public boolean updateResourceIdentifier(Resource resource, UUID oldIdentifier, UUID newIdentifier) {
        switch (resource) {
        case PRINCIPAL:
            return principalDao.updateResourceIdentifier(oldIdentifier, newIdentifier);
        case ANNOTATION:
            return annotationDao.updateResourceIdentifier(oldIdentifier, newIdentifier);
        case TARGET:
            return targetDao.updateResourceIdentifier(oldIdentifier, newIdentifier);
        case CACHED_REPRESENTATION:
            return cachedRepresentationDao.updateResourceIdentifier(oldIdentifier, newIdentifier);
        case NOTEBOOK:
            return notebookDao.updateResourceIdentifier(oldIdentifier, newIdentifier);
        default:
            return false;
        }
    }

    @Override
    public boolean updateAccount(UUID principalExternalID, String account) throws NotInDataBaseException {
        return principalDao.updateAccount(principalExternalID, account);
    }

    @Override
    public int updatePermission(Number annotationID, Number principalID, Access access) {
        int result;
        if (access != null) {
            Boolean checkAccess = annotationDao.hasExplicitAccess(annotationID, principalID);
            if (checkAccess) {
                result = annotationDao.updatePermission(annotationID, principalID, access);
            } else {
                result = annotationDao.addPermission(annotationID, principalID, access);
            }
        } else {
            result = annotationDao.deletePermission(annotationID, principalID);
        }
        return result;
    }

    @Override
    public int updatePublicAttribute(Number annotationID, Access publicAttribute) {
        return annotationDao.updatePublicAccess(annotationID, publicAttribute);
    }

    @Override
    public int updateOrAddPermissions(Number annotationID, PermissionList permissionList)
            throws NotInDataBaseException {
        annotationDao.updatePublicAccess(annotationID, permissionList.getPublic());
        List<Permission> permissions = permissionList.getPermission();
        int result = 0;
        for (Permission permission : permissions) {
            Number principalID = principalDao.getInternalIDFromHref(permission.getPrincipalHref());
            Access access = permission.getLevel();
            Boolean checkAccess = annotationDao.hasExplicitAccess(annotationID, principalID);
            if (checkAccess) {
                result = result + annotationDao.updatePermission(annotationID, principalID, access);
            } else {
                result = result + annotationDao.addPermission(annotationID, principalID, access);
            }
        }
        return result;
    }

    // TODO: optimize (not chnanged targets should not be deleted)
    @Override
    public int updateAnnotation(Annotation annotation, String remoteUser)
            throws NotInDataBaseException, ForbiddenException {

        Number annotationID = annotationDao.getInternalID(UUID.fromString(annotation.getId()));
        Number ownerID = principalDao.getInternalIDFromHref(annotation.getOwnerHref());
        Number remoteUserID = principalDao.getPrincipalInternalIDFromRemoteID(remoteUser);

        boolean isOwner = ownerID.equals(remoteUserID);
        boolean hasAllAccess = annotationDao.getAccess(annotationID, remoteUserID).equals(Access.ALL);
        boolean isAdmin = remoteUserID.equals(principalDao.getDBAdminID());
        boolean weakPrincipal = (!isOwner && !hasAllAccess && !isAdmin);

        if (weakPrincipal) { // we need to check if permissions are intact
            if (!(annotation.getPermissions().getPublic()).equals(annotationDao.getPublicAttribute(annotationID))) {
                throw new ForbiddenException(
                        "The inlogged user does not have rights to update 'public' attribute in this annotation.");
            }
            List<Map<Number, String>> permissionsDB = annotationDao.getPermissions(annotationID);
            if (!this.permissionsIntact(annotation.getPermissions().getPermission(), permissionsDB)) {
                throw new ForbiddenException(
                        "The inlogged user does not have rights to update permissions in this annotation.");
            }
        }

        int updatedAnnotations = annotationDao.updateAnnotation(annotation, annotationID, ownerID);
        int deletedTargets = annotationDao.deleteAllAnnotationTarget(annotationID);
        int addedTargets = this.addTargets(annotation, annotationID);
        if (!weakPrincipal) { // if weak permissions reach this point then permissions are the same
            int changedPermissions = this.updateOrAddPermissions(annotationID, annotation.getPermissions());
        }
        return updatedAnnotations;
    }

    private boolean permissionsIntact(List<Permission> permissionsInput, List<Map<Number, String>> permissionsDB)
            throws NotInDataBaseException {
        if (permissionsInput == null || permissionsInput.isEmpty()) {
            return true;
        }

        if (permissionsDB == null || permissionsDB.isEmpty()) {
            return false;
        }

        for (Permission permission : permissionsInput) {
            Number principalID = principalDao.getInternalIDFromHref(permission.getPrincipalHref());
            String accessLevel = permission.getLevel().value();
            Map current = new HashMap<Number, String>();
            current.put(principalID, accessLevel);
            int index = permissionsDB.indexOf(current);
            if (index > -1) {
                if (!accessLevel.equals(permissionsDB.get(index).get(principalID))) {
                    return false;
                }
            } else {
                if (!accessLevel.equals(Access.NONE.value())) {
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public int updateAnnotationBody(Number internalID, AnnotationBody annotationBody) {
        String[] body = annotationDao.retrieveBodyComponents(annotationBody);
        return annotationDao.updateAnnotationBody(internalID, body[0], body[1],
                annotationBody.getXmlBody() != null);
    }

    @Override
    public int updateAnnotationHeadline(Number internalID, String newHeader) {
        return annotationDao.updateAnnotationHeadline(internalID, newHeader);
    }

    @Override
    public Number updatePrincipal(Principal principal) throws NotInDataBaseException {
        return principalDao.updatePrincipal(principal);
    }

    @Override
    public int updateTargetCachedFragment(Number targetID, Number cachedID, String fragmentDescriptor) {
        return targetDao.updateTargetCachedRepresentationFragment(targetID, cachedID, fragmentDescriptor);
    }

    @Override
    public int updateCachedMetada(CachedRepresentationInfo cachedInfo) throws NotInDataBaseException {
        Number internalID = cachedRepresentationDao.getInternalID(UUID.fromString(cachedInfo.getId()));
        return cachedRepresentationDao.updateCachedRepresentationMetadata(internalID, cachedInfo);
    }

    @Override
    public int updateCachedBlob(Number internalID, InputStream cachedBlob) throws IOException {
        return cachedRepresentationDao.updateCachedRepresentationBlob(internalID, cachedBlob);
    }

    /// notebooks ///
    @Override
    public boolean updateNotebookMetadata(Number notebookID, NotebookInfo upToDateNotebookInfo)
            throws NotInDataBaseException {
        Number ownerID = principalDao.getInternalIDFromHref(upToDateNotebookInfo.getOwnerHref());
        return notebookDao.updateNotebookMetadata(notebookID, upToDateNotebookInfo.getTitle(), ownerID);
    }

    @Override
    public boolean addAnnotationToNotebook(Number notebookID, Number annotationID) {
        return notebookDao.addAnnotationToNotebook(notebookID, annotationID);
    }

    /////////////// ADDERS  /////////////////////////////////
    @Override
    public Number[] addCachedForTarget(Number targetID, String fragmentDescriptor,
            CachedRepresentationInfo cachedInfo, InputStream cachedBlob)
            throws NotInDataBaseException, IOException {
        Number[] result = new Number[2];
        try {
            result[1] = cachedRepresentationDao.addCachedRepresentation(cachedInfo, cachedBlob);
        } catch (NotInDataBaseException e1) {
            logger.info("Something wrong went while adding cached.");
            throw e1;
        }

        result[0] = targetDao.addTargetCachedRepresentation(targetID, result[1], fragmentDescriptor);
        return result;

    }

    @Override
    public Map<String, String> addTargetsForAnnotation(Number annotationID, List<TargetInfo> targets)
            throws NotInDataBaseException {
        Map<String, String> result = new HashMap<String, String>();
        for (TargetInfo targetInfo : targets) {
            try {
                Number targetIDRunner = targetDao.getInternalIDFromHref(targetInfo.getHref());
                int affectedRows = annotationDao.addAnnotationTarget(annotationID, targetIDRunner);
            } catch (NotInDataBaseException e) {
                Target newTarget = this.createFreshTarget(targetInfo);
                Number targetID = targetDao.addTarget(newTarget);
                String targetTemporaryId = targetInfo.getHref();
                result.put(targetTemporaryId, targetDao.getHrefFromInternalID(targetID));
                int affectedRows = annotationDao.addAnnotationTarget(annotationID, targetID);
            }
        }
        return result;
    }

    @Override
    public Number addPrincipalsAnnotation(Number ownerID, Annotation annotation) throws NotInDataBaseException {
        Number annotationID = annotationDao.addAnnotation(annotation, ownerID);
        int affectedAnnotRows = this.addTargets(annotation, annotationID);
        int addedPrincipalsAccesss = this.addPermissions(annotation.getPermissions().getPermission(), annotationID);
        return annotationID;
    }

    @Override
    public Number addPrincipal(Principal principal, String remoteID)
            throws NotInDataBaseException, PrincipalExists {
        if (principalDao.principalExists(remoteID)) {
            throw new PrincipalExists(remoteID);
        } else {
            return principalDao.addPrincipal(principal, remoteID);
        }
    }

    //////////// notebooks //////
    @Override
    public Number createNotebook(Notebook notebook, Number ownerID) throws NotInDataBaseException {
        Number notebookID = notebookDao.createNotebookWithoutAccesssAndAnnotations(notebook, ownerID);
        boolean updateOwner = notebookDao.setOwner(notebookID, ownerID);
        List<Permission> permissions = notebook.getPermissions().getPermission();
        for (Permission permission : permissions) {
            Number principalID = principalDao.getInternalIDFromHref(permission.getPrincipalHref());
            Access access = permission.getLevel();
            boolean updateAccesss = notebookDao.addAccessToNotebook(notebookID, principalID, access);
        }
        return notebookID;
    }

    @Override
    public boolean createAnnotationInNotebook(Number notebookID, Annotation annotation, Number ownerID)
            throws NotInDataBaseException {
        Number newAnnotationID = this.addPrincipalsAnnotation(ownerID, annotation);
        return notebookDao.addAnnotationToNotebook(notebookID, newAnnotationID);
    }

    @Override
    public int addSpringUser(String username, String password, int strength, String salt) {
        int users = principalDao.addSpringUser(username, password, strength, salt);
        int authorities = principalDao.addSpringAuthorities(username);
        return users + authorities;
    }

    ////////////// DELETERS //////////////////
    @Override
    public int deletePrincipal(Number principalID) throws PrincipalCannotBeDeleted {
        return principalDao.deletePrincipal(principalID);
    }

    @Override
    public int deleteCachedRepresentation(Number internalID) {

        if (targetDao.cachedIsInUse(internalID)) {
            logger.debug("Cached Repr. is in use, and cannot be deleted.");
            return 0;
        }

        return cachedRepresentationDao.deleteCachedRepresentation(internalID);
    }

    @Override
    public int[] deleteCachedRepresentationOfTarget(Number targetID, Number cachedID) {
        int[] result = new int[2];
        result[0] = targetDao.deleteTargetCachedRepresentation(targetID, cachedID);
        if (result[0] > 0) {
            result[1] = cachedRepresentationDao.deleteCachedRepresentation(cachedID);
        } else {
            result[1] = 0;

        }
        return result;
    }

    @Override
    public int[] deleteAllCachedRepresentationsOfTarget(Number targetID) {
        int[] result = new int[2];
        result[0] = 0;
        result[1] = 0;
        List<Number> cachedIDs = cachedRepresentationDao.getCachedRepresentationsForTarget(targetID);
        for (Number cachedID : cachedIDs) {
            int[] currentResult = this.deleteCachedRepresentationOfTarget(targetID, cachedID);
            result[0] = result[0] + currentResult[0];
            result[1] = result[1] + currentResult[1];
        }
        return result;
    }

    @Override
    public int[] deleteAnnotation(Number annotationID) {
        int[] result = new int[5];
        result[1] = annotationDao.deletePermissions(annotationID);
        List<Number> targetIDs = targetDao.getTargetIDs(annotationID);
        result[2] = annotationDao.deleteAllAnnotationTarget(annotationID);
        result[3] = 0;
        if (targetIDs != null) {
            for (Number targetID : targetIDs) {
                this.deleteAllCachedRepresentationsOfTarget(targetID);
                result[3] = result[3] + this.deleteTarget(targetID);

            }
        }

        result[4] = annotationDao.deleteAnnotationFromAllNotebooks(annotationID);

        result[0] = annotationDao.deleteAnnotation(annotationID);
        return result;
    }

    @Override
    public int deleteTarget(Number internalID) {
        if (annotationDao.targetIsInUse(internalID)) {
            logger.debug("The target is in use, and cannot be deleted.");
            return 0;
        }
        return targetDao.deleteTarget(internalID);

    }

    @Override
    public boolean deleteNotebook(Number notebookID) {
        if (notebookDao.deleteAllAccesssForNotebook(notebookID)
                || notebookDao.deleteAllAnnotationsFromNotebook(notebookID)) {
            return notebookDao.deleteNotebook(notebookID);
        } else {
            return false;
        }
    }

    @Override
    public int deleteAnnotationPrincipalAccess(Number annotationID, Number principalID) {
        return annotationDao.deletePermission(annotationID, principalID);
    }
    ////////////// HELPERS ////////////////////
    ////////////////////////////////////////

    @Override
    public ResponseBody makeAnnotationResponseEnvelope(Number annotationID) {
        ResponseBody result = new ResponseBody();
        Annotation annotation = this.getAnnotation(annotationID);
        result.setAnnotation(annotation);
        List<String> targetsNoCached = this.getTargetsWithNoCachedRepresentation(annotationID);
        ActionList actionList = new ActionList();
        result.setActionList(actionList);
        actionList.getAction()
                .addAll(makeActionList(targetsNoCached, AnnotationActionName.CREATE_CACHED_REPRESENTATION.value()));
        return result;
    }

    @Override
    public ResponseBody makeNotebookResponseEnvelope(Number notebookID) {
        ResponseBody result = new ResponseBody();
        result.setPermissions(null);
        Notebook notebook = this.getNotebook(notebookID);
        result.setNotebook(notebook);
        return result;
    }

    @Override
    public ResponseBody makeAccessResponseEnvelope(Number resourceID, Resource resource) {
        ResponseBody result = new ResponseBody();
        PermissionList permissions = this.getPermissions(resourceID, resource);
        result.setPermissions(permissions);
        List<String> principalsWithNoInfo = this.getPrincipalsWithNoInfo(resourceID);
        ActionList actionList = new ActionList();
        result.setActionList(actionList);
        actionList.getAction()
                .addAll(makeActionList(principalsWithNoInfo, PermissionActionName.PROVIDE_PRINCIPAL_INFO.value()));
        return result;
    }

    private List<Action> makeActionList(List<String> resourceURIs, String message) {
        if (resourceURIs != null) {
            if (resourceURIs.isEmpty()) {
                return (new ArrayList<Action>());
            } else {
                List<Action> result = new ArrayList<Action>();
                for (String resourceURI : resourceURIs) {
                    Action action = new Action();
                    result.add(action);
                    action.setMessage(message);
                    action.setObject(resourceURI);
                }
                return result;
            }
        } else {
            return null;
        }
    }

    @Override
    public UUID getPrincipalExternalIdFromName(String fullName) throws NotInDataBaseException {
        return principalDao.getExternalIdFromName(fullName);
    }

    @Override
    public List<UUID> getAnnotationExternalIdsFromHeadline(String headline) {
        return annotationDao.getExternalIdFromHeadline(headline);
    }

    @Override
    public List<Number> getAnnotationInternalIDsFromHeadline(String headline) {
        return annotationDao.getInternalIDsFromHeadline(headline);
    }

    //// privee ///
    private Target createFreshTarget(TargetInfo targetInfo) {
        Target target = new Target();
        target.setLink(targetInfo.getLink());
        target.setVersion(targetInfo.getVersion());
        return target;
    }

    private int addTargets(Annotation annotation, Number annotationID) throws NotInDataBaseException {
        List<TargetInfo> targets = annotation.getTargets().getTargetInfo();
        Map<String, String> targetIdPairs = this.addTargetsForAnnotation(annotationID, targets);
        AnnotationBody annotationBody = annotation.getBody();
        String bodyText;
        String newBodyText;
        String mimeType;
        if (annotationBody.getXmlBody() != null) {
            bodyText = Helpers.elementToString(annotation.getBody().getXmlBody().getAny());
            mimeType = annotationBody.getXmlBody().getMimeType();
        } else {
            if (annotation.getBody().getTextBody() != null) {
                bodyText = annotation.getBody().getTextBody().getBody();
                mimeType = annotationBody.getTextBody().getMimeType();
            } else {
                logger.error("The client has sent ill-formed annotation body.");
                return -1;
            }
        }
        newBodyText = Helpers.replace(bodyText, targetIdPairs);
        return annotationDao.updateAnnotationBody(annotationID, newBodyText, mimeType,
                annotationBody.getXmlBody() != null);
    }

    private int addPermissions(List<Permission> permissions, Number annotationID) throws NotInDataBaseException {
        if (permissions != null) {
            int addedPermissions = 0;
            for (Permission permission : permissions) {
                addedPermissions = addedPermissions + annotationDao.addPermission(annotationID,
                        principalDao.getInternalIDFromHref(permission.getPrincipalHref()), permission.getLevel());
            }
            return addedPermissions;
        } else {
            return 0;
        }
    }

    private TargetInfo getTargetInfoFromTarget(Target target) {
        TargetInfo targetInfo = new TargetInfo();
        targetInfo.setHref(target.getHref());
        targetInfo.setLink(target.getLink());
        targetInfo.setVersion(target.getVersion());
        return targetInfo;
    }
}