001    /**
002     * 
003     * $Revision: 15422 $ $Date: 2009-07-01 10:48:51 +0200 (Wed, 01 Jul 2009) $
004     *
005     * This file is part of ** M y C o R e **
006     * Visit our homepage at http://www.mycore.de/ for details.
007     *
008     * This program is free software; you can use it, redistribute it
009     * and / or modify it under the terms of the GNU General Public License
010     * (GPL) as published by the Free Software Foundation; either version 2
011     * of the License or (at your option) any later version.
012     *
013     * This program is distributed in the hope that it will be useful, but
014     * WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program, normally in the file license.txt.
020     * If not, write to the Free Software Foundation Inc.,
021     * 59 Temple Place - Suite 330, Boston, MA  02111-1307 USA
022     *
023     **/
024    package org.mycore.datamodel.classifications2.impl;
025    
026    import java.util.ArrayList;
027    import java.util.BitSet;
028    import java.util.Collection;
029    import java.util.HashMap;
030    import java.util.HashSet;
031    import java.util.List;
032    import java.util.Map;
033    
034    import org.apache.log4j.Logger;
035    import org.hibernate.Criteria;
036    import org.hibernate.Query;
037    import org.hibernate.Session;
038    import org.hibernate.criterion.Order;
039    import org.hibernate.criterion.Projections;
040    import org.mycore.backend.hibernate.MCRHIBConnection;
041    import org.mycore.common.MCRCache;
042    import org.mycore.common.MCRConfiguration;
043    import org.mycore.common.MCRPersistenceException;
044    import org.mycore.datamodel.classifications2.MCRCategLinkService;
045    import org.mycore.datamodel.classifications2.MCRCategory;
046    import org.mycore.datamodel.classifications2.MCRCategoryDAOFactory;
047    import org.mycore.datamodel.classifications2.MCRCategoryID;
048    import org.mycore.datamodel.classifications2.MCRObjectReference;
049    
050    /**
051     * 
052     * @author Thomas Scheffler (yagee)
053     * 
054     * @version $Revision: 15422 $ $Date: 2009-07-01 10:48:51 +0200 (Wed, 01 Jul 2009) $
055     * @since 2.0
056     */
057    public class MCRCategLinkServiceImpl implements MCRCategLinkService {
058    
059        private static MCRHIBConnection HIB_CONNECTION_INSTANCE;
060    
061        private static Logger LOGGER = Logger.getLogger(MCRCategLinkServiceImpl.class);
062    
063        private static Class<MCRCategoryLink> LINK_CLASS = MCRCategoryLink.class;
064    
065        private static MCRCache categCache = new MCRCache(MCRConfiguration.instance().getInt(
066                "MCR.Classifications.LinkServiceImpl.CategCache.Size", 1000), "MCRCategLinkService category cache");
067    
068        private static MCRCategoryDAOImpl DAO = new MCRCategoryDAOImpl();
069    
070        public MCRCategLinkServiceImpl() {
071            HIB_CONNECTION_INSTANCE = MCRHIBConnection.instance();
072        }
073    
074        public Map<MCRCategoryID, Number> countLinks(MCRCategory parent, boolean childrenOnly) {
075            return countLinksForType(parent, null, childrenOnly);
076        }
077    
078        public Map<MCRCategoryID, Number> countLinksForType(MCRCategory parent, String type, boolean childrenOnly) {
079            boolean restrictedByType = (type != null);
080            String queryName;
081            if (childrenOnly) {
082                queryName = restrictedByType ? ".NumberByTypePerChildOfParentID" : ".NumberPerChildOfParentID";
083            } else {
084                queryName = restrictedByType ? ".NumberByTypePerClassID" : ".NumberPerClassID";
085            }
086            Map<MCRCategoryID, Number> countLinks = new HashMap<MCRCategoryID, Number>();
087            Collection<MCRCategoryID> ids = childrenOnly ? getAllChildIDs(parent) : getAllCategIDs(parent);
088            for (MCRCategoryID id : ids) {
089                // initialize all categIDs with link count of zero
090                countLinks.put(id, 0);
091            }
092            //have to use rootID here if childrenOnly=false
093            //old classification browser/editor could not determine links correctly otherwise 
094            if (!childrenOnly) {
095                parent = parent.getRoot();
096            } else if (!(parent instanceof MCRCategoryImpl) || ((MCRCategoryImpl) parent).getInternalID() == 0) {
097                final Session session = MCRHIBConnection.instance().getSession();
098                parent = MCRCategoryDAOImpl.getByNaturalID(session, parent.getId());
099            }
100            LOGGER.info("parentID:" + parent.getId());
101            String classID = parent.getId().getRootID();
102            Query q = HIB_CONNECTION_INSTANCE.getNamedQuery(LINK_CLASS.getName() + queryName);
103            // query can take long time, please cache result
104            q.setCacheable(true);
105            q.setParameter("classID", classID);
106            if (childrenOnly) {
107                q.setParameter("parentID", ((MCRCategoryImpl) parent).getInternalID());
108            }
109            if (restrictedByType) {
110                q.setParameter("type", type);
111            }
112            // get object count for every category (not accumulated)
113            @SuppressWarnings("unchecked")
114            List<Object[]> result = q.list();
115            for (Object[] sr : result) {
116                MCRCategoryID key = new MCRCategoryID(classID, sr[0].toString());
117                Number value = (Number) sr[1];
118                countLinks.put(key, value);
119            }
120            return countLinks;
121        }
122    
123        public void deleteLink(String id) {
124            Query q = HIB_CONNECTION_INSTANCE.getNamedQuery(LINK_CLASS.getName() + ".deleteByObjectID");
125            q.setParameter("id", id);
126            int deleted = q.executeUpdate();
127            LOGGER.debug("Number of Links deleted: " + deleted);
128        }
129    
130        public void deleteLinks(Collection<String> ids) {
131            Query q = HIB_CONNECTION_INSTANCE.getNamedQuery(LINK_CLASS.getName() + ".deleteByObjectCollection");
132            q.setParameterList("ids", ids);
133            int deleted = q.executeUpdate();
134            LOGGER.debug("Number of Links deleted: " + deleted);
135        }
136    
137        @SuppressWarnings("unchecked")
138        public Collection<String> getLinksFromCategory(MCRCategoryID id) {
139            Query q = HIB_CONNECTION_INSTANCE.getNamedQuery(LINK_CLASS.getName() + ".ObjectIDByCategory");
140            q.setCacheable(true);
141            q.setParameter("rootID", id.getRootID());
142            q.setParameter("categID", id.getID());
143            return q.list();
144        }
145    
146        @SuppressWarnings("unchecked")
147        public Collection<String> getLinksFromCategoryForType(MCRCategoryID id, String type) {
148            Query q = HIB_CONNECTION_INSTANCE.getNamedQuery(LINK_CLASS.getName() + ".ObjectIDByCategoryAndType");
149            q.setCacheable(true);
150            q.setParameter("rootID", id.getRootID());
151            q.setParameter("categID", id.getID());
152            q.setParameter("type", type);
153            return q.list();
154        }
155    
156        @SuppressWarnings("unchecked")
157        public Collection<MCRCategoryID> getLinksFromObject(String id) {
158            Query q = HIB_CONNECTION_INSTANCE.getNamedQuery(LINK_CLASS.getName() + ".categoriesByObjectID");
159            q.setCacheable(true);
160            q.setParameter("id", id);
161            List<Object[]> result = q.list();
162            ArrayList<MCRCategoryID> returns = new ArrayList<MCRCategoryID>(result.size());
163            for (Object[] idValues : result) {
164                returns.add(new MCRCategoryID(idValues[0].toString(), idValues[1].toString()));
165            }
166            return returns;
167        }
168    
169        public void setLinks(MCRObjectReference objectReference, Collection<MCRCategoryID> categories) {
170            Session session = HIB_CONNECTION_INSTANCE.getSession();
171            for (MCRCategoryID categID : categories) {
172                final MCRCategoryImpl category = getMCRCategory(session, categID);
173                if (category == null)
174                    throw new MCRPersistenceException("Could not link to unknown category " + categID);
175                MCRCategoryLink link = new MCRCategoryLink(category, objectReference);
176                LOGGER.debug("Adding Link from " + link.getCategory().getId() + "(" + link.getCategory().getInternalID() + ") to "
177                        + objectReference.getObjectID());
178                session.save(link);
179                LOGGER.debug("===DONE: " + link.id);
180            }
181        }
182    
183        private static MCRCategoryImpl getMCRCategory(Session session, MCRCategoryID categID) {
184            MCRCategoryImpl categ = (MCRCategoryImpl) categCache.getIfUpToDate(categID, DAO.getLastModified());
185            if (categ != null)
186                return categ;
187            categ = MCRCategoryDAOImpl.getByNaturalID(session, categID);
188            if (categ == null) {
189                return null;
190            }
191            categCache.put(categID, categ);
192            return categ;
193        }
194    
195        public Map<MCRCategoryID, Boolean> hasLinks(MCRCategory category) {
196            HashMap<MCRCategoryID, Boolean> boolMap = new HashMap<MCRCategoryID, Boolean>();
197            final BitSet linkedInternalIds = getLinkedInternalIds();
198            MCRCategoryImpl rootImpl = (MCRCategoryImpl) MCRCategoryDAOFactory.getInstance().getCategory(category.getRoot().getId(), -1);
199            storeHasLinkValues(boolMap, linkedInternalIds, rootImpl);
200            return boolMap;
201        }
202    
203        private void storeHasLinkValues(HashMap<MCRCategoryID, Boolean> boolMap, BitSet internalIds, MCRCategoryImpl parent) {
204            final int internalID = parent.getInternalID();
205            if (internalID < internalIds.size() && internalIds.get(internalID)) {
206                addParentHasValues(boolMap, parent);
207            } else {
208                boolMap.put(parent.getId(), false);
209            }
210            for (MCRCategory child : parent.getChildren()) {
211                storeHasLinkValues(boolMap, internalIds, (MCRCategoryImpl) child);
212            }
213        }
214    
215        private void addParentHasValues(HashMap<MCRCategoryID, Boolean> boolMap, MCRCategory parent) {
216            boolMap.put(parent.getId(), true);
217            if (parent.isCategory() && !boolMap.get(parent.getParent().getId()).booleanValue()) {
218                addParentHasValues(boolMap, parent.getParent());
219            }
220        }
221    
222        private BitSet getLinkedInternalIds() {
223            Session session = HIB_CONNECTION_INSTANCE.getSession();
224            Criteria criteria = session.createCriteria(LINK_CLASS);
225            criteria.setProjection(Projections.distinct(Projections.property("category.internalID")));
226            criteria.addOrder(Order.desc("category.internalID"));
227            @SuppressWarnings("unchecked")
228            List<Number> result = criteria.list();
229            int maxSize = result.size() == 0 ? 1 : result.get(0).intValue() + 1;
230            BitSet linkSet = new BitSet(maxSize);
231            for (Number internalID : result) {
232                linkSet.set(internalID.intValue(), true);
233            }
234            return linkSet;
235        }
236    
237        private static Collection<MCRCategoryID> getAllCategIDs(MCRCategory category) {
238            HashSet<MCRCategoryID> ids = new HashSet<MCRCategoryID>();
239            ids.add(category.getId());
240            for (MCRCategory cat : category.getChildren()) {
241                ids.addAll(getAllCategIDs(cat));
242            }
243            return ids;
244        }
245    
246        private static Collection<MCRCategoryID> getAllChildIDs(MCRCategory category) {
247            HashSet<MCRCategoryID> ids = new HashSet<MCRCategoryID>();
248            for (MCRCategory cat : category.getChildren()) {
249                ids.add(cat.getId());
250            }
251            return ids;
252        }
253    }