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 }