001    /**
002     * $RCSfile: MCRClassificationEditor.java,v $
003     * $Revision: 15496 $ $Date: 2009-07-10 16:28:14 +0200 (Fri, 10 Jul 2009) $
004     *
005     * This file is part of ***  M y C o R e  *** 
006     * See 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, in a file called gpl.txt or 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    
025    package org.mycore.datamodel.classifications;
026    
027    import static org.jdom.Namespace.XML_NAMESPACE;
028    import static org.mycore.common.MCRConstants.XSI_NAMESPACE;
029    
030    import java.io.File;
031    import java.io.FileOutputStream;
032    import java.util.Collection;
033    import java.util.List;
034    
035    import org.apache.commons.fileupload.FileItem;
036    import org.apache.log4j.Logger;
037    import org.jdom.Document;
038    import org.jdom.Element;
039    import org.jdom.output.XMLOutputter;
040    import org.mycore.common.MCRConfiguration;
041    import org.mycore.common.MCRException;
042    import org.mycore.common.MCRSessionMgr;
043    import org.mycore.common.MCRUtils;
044    import org.mycore.common.xml.MCRXMLHelper;
045    import org.mycore.datamodel.classifications2.MCRCategLinkServiceFactory;
046    import org.mycore.datamodel.classifications2.MCRCategory;
047    import org.mycore.datamodel.classifications2.MCRCategoryDAO;
048    import org.mycore.datamodel.classifications2.MCRCategoryDAOFactory;
049    import org.mycore.datamodel.classifications2.MCRCategoryID;
050    import org.mycore.datamodel.classifications2.MCRLabel;
051    import org.mycore.datamodel.classifications2.utils.MCRXMLTransformer;
052    
053    /**
054     * This class implements all methods for a edit, modify delete categories in
055     * classification and the classification itself
056     * 
057     * @author Anja Schaar
058     * @author Jens Kupferschmidt
059     * @version
060     */
061    
062    public class MCRClassificationEditor {
063    
064        // logger
065        private static Logger LOGGER = Logger.getLogger(MCRClassificationEditor.class);
066    
067        private MCRCategory classif;
068    
069        private static MCRCategoryDAO DAO = MCRCategoryDAOFactory.getInstance();
070    
071        private MCRConfiguration CONFIG;
072    
073        private File fout;
074    
075        public MCRClassificationEditor() {
076            CONFIG = MCRConfiguration.instance();
077        }
078    
079        /**
080         * Create an new category in the category path.
081         * 
082         * @param indoc
083         *            the output from the editor dialogue
084         * @param clid
085         *            the classification ID
086         * @param categid
087         *            the category ID to add after it
088         * @return
089         */
090        public boolean createCategoryInClassification(org.jdom.Document indoc, MCRCategoryID id) {
091    
092            try {
093                Element clroot = indoc.getRootElement();
094                if (clroot == null) {
095                    return false;
096                }
097                Element categories = (Element) clroot.getChild("categories");
098                if (categories == null)
099                    return false;
100    
101                Element newCateg = (Element) categories.getChild("category").clone();
102                MCRCategoryID newID = new MCRCategoryID(id.getRootID(), newCateg.getAttributeValue("ID"));
103                classif = MCRClassificationBrowserData.getClassificationPool().getClassificationAsPojo(MCRCategoryID.rootID(id.getRootID()),
104                        true);
105    
106                // check the new category entry
107                if (newID.getID().equalsIgnoreCase(id.getID())) {
108                    LOGGER.error("The category ID's are not different.");
109                    return false;
110                }
111    
112                final MCRCategory findCategory = findCategory(classif, newID);
113                if (findCategory == null) {
114                    if (!id.getID().equals("empty")) {
115    
116                        MCRCategory prevCateg = findCategory(classif, id);
117                        LOGGER.debug("Previous Category: " + prevCateg.getId() + " found.");
118    
119                        MCRXMLTransformer.buildCategory(id.getRootID(), newCateg, prevCateg);
120                        MCRClassificationBrowserData.getClassificationPool().updateClassification(classif);
121                        String sessionID = MCRSessionMgr.getCurrentSession().getID();
122                        MCRClassificationBrowserData.ClassUserTable.put(classif.getId().getRootID(), sessionID);
123                        return true;
124                    } else {
125                        MCRCategory newCategory = MCRXMLTransformer.buildCategory(id.getRootID(), newCateg, classif);
126                        LOGGER.debug("Adding category:" + newCategory.getId() + " to classification: " + classif.getId());
127                        MCRClassificationBrowserData.getClassificationPool().updateClassification(classif);
128                        String sessionID = MCRSessionMgr.getCurrentSession().getID();
129                        MCRClassificationBrowserData.ClassUserTable.put(classif.getId().getRootID(), sessionID);
130                        return true;
131                    }
132                } else {
133                    LOGGER.error("The category " + newID + " does already exist.");
134                    return false;
135                }
136            } catch (Exception e1) {
137                e1.printStackTrace();
138                LOGGER.error("Classification creation fails. Reason is:" + e1.getMessage());
139                return false;
140            }
141    
142        }
143    
144        /**
145         * Replace category data like label(s) and url.
146         * 
147         * @param indoc
148         *            the output from the editor dialogue
149         * @param clid
150         *            the classification ID
151         * @param categid
152         *            the category ID
153         * @return true if all it's okay, else return false
154         */
155        public final boolean modifyCategoryInClassification(org.jdom.Document indoc, MCRCategoryID id) {
156            try {
157                LOGGER.debug("Start modify category in classification " + id.getRootID() + " with categid " + id.getID());
158                Element clroot = indoc.getRootElement();
159                Element newCateg = (Element) clroot.getChild("categories").getChild("category").clone();
160                String newID = newCateg.getAttributeValue("ID");
161                classif = MCRClassificationBrowserData.getClassificationPool().getClassificationAsPojo(MCRCategoryID.rootID(id.getRootID()),
162                        true);
163                // check the category entry
164                if (!newID.equalsIgnoreCase(id.getID())) {
165                    LOGGER.error("The category ID's are different.");
166                    return false;
167                }
168    
169                MCRCategory oldCategory = findCategory(classif, id);
170                if (oldCategory == null) {
171                    LOGGER.error("The category ID " + id.getID() + " does not exist in classification " + id.getRootID());
172                    return false;
173                }
174    
175                MCRCategory newCategory = MCRXMLTransformer.buildCategory(id.getRootID(), newCateg, oldCategory.getParent());
176                //copy new values to old copy of category
177                oldCategory.setURI(newCategory.getURI());
178                oldCategory.getLabels().clear();
179                Collection<MCRLabel> labels = newCategory.getLabels();
180                oldCategory.getLabels().addAll(labels);
181    
182                MCRClassificationBrowserData.getClassificationPool().updateClassification(classif);
183                String sessionID = MCRSessionMgr.getCurrentSession().getID();
184                MCRClassificationBrowserData.ClassUserTable.put(classif.getId().getRootID(), sessionID);
185    
186                return true;
187            } catch (Exception e1) {
188                LOGGER.error("Category modify fails. Reason is:");
189                e1.printStackTrace();
190                return false;
191            }
192        }
193    
194        public boolean isLocked(String classid) {
195            if (MCRClassificationBrowserData.ClassUserTable.containsKey(classid)) {
196                String lockedSessionID = MCRClassificationBrowserData.ClassUserTable.get(classid);
197                String currentSessionID = MCRSessionMgr.getCurrentSession().getID();
198                if (!lockedSessionID.equals(currentSessionID)) {
199                    return true;
200                }
201            }
202            return false;
203        }
204    
205        /**
206         * Create or update a classification form import.
207         * 
208         * @param bUpdate
209         *            true if this operation should be a update, else false
210         * @param fileName
211         *            the name of classification file
212         * @return true if all it's okay, else return false
213         */
214        public boolean importClassification(boolean bUpdate, String fileName) {
215            LOGGER.debug("Start importNewClassification.");
216            try {
217                try {
218                    File file = new File(fileName);
219                    LOGGER.info("Reading file " + file + " ...\n");
220                    Document jdom = MCRXMLHelper.parseURI(file.toURI());
221                    MCRCategory classification = MCRXMLTransformer.getCategory(jdom);
222    
223                    MCRClassificationBrowserData.getClassificationPool().updateClassification(classification);
224    
225                    return true;
226                } catch (MCRException ex) {
227                    LOGGER.error("Exception while loading from file " + fileName, ex);
228                    return false;
229                }
230            } catch (Exception e1) {
231                LOGGER.error("Classification import fails. Reason is:" + e1.getMessage());
232                return false;
233            }
234    
235        }
236    
237        /**
238         * Create a new classification with the data from the editor dialogue.
239         * 
240         * @param indoc
241         *            the output from the editor dialogue
242         * @return true if all it's okay, else return false
243         */
244        public final boolean createNewClassification(org.jdom.Document indoc) {
245            try {
246                LOGGER.debug("Start create a  new classification.");
247                Element clroot = indoc.getRootElement();
248                XMLOutputter out = new XMLOutputter();
249                out.output(indoc, System.out);
250    
251                Element mycoreclass = new Element("mycoreclass");
252                Element categories = new Element("categories");
253                Element category = new Element("category");
254                category.setAttribute("ID", "empty");
255                Element label = new Element("label");
256                label.setAttribute("text", "empty");
257                label.setAttribute("description", "empty");
258                label.setAttribute("lang", CONFIG.getString("MCR.Metadata.DefaultLang"), XML_NAMESPACE);
259                category.addContent(label);
260                categories.addContent(category);
261    
262                String submittedID = indoc.getRootElement().getAttributeValue("ID");
263                MCRCategoryID cli = null;
264    
265                if (!MCRClassificationBrowserData.getClassificationPool().getAllIDs().contains(MCRCategoryID.rootID(submittedID))) {
266                    cli = MCRCategoryID.rootID(submittedID);
267                } else {
268                    LOGGER.error("Create an unique ID failed. " + submittedID);
269                    return false;
270                }
271    
272                mycoreclass.addNamespaceDeclaration(XSI_NAMESPACE);
273                mycoreclass.setAttribute("noNamespaceSchemaLocation", "MCRClassification.xsd", XSI_NAMESPACE);
274                mycoreclass.setAttribute("ID", cli.getRootID());
275                mycoreclass.setAttribute("counter", "0");
276                @SuppressWarnings("unchecked")
277                List<Element> tagList = clroot.getChildren("label");
278                for (Element element : tagList) {
279                    Element newE = new Element("label");
280                    newE.setAttribute("lang", element.getAttributeValue("lang"), XML_NAMESPACE);
281                    newE.setAttribute("text", element.getAttributeValue("text"));
282                    if (element.getAttributeValue("description") != null) {
283                        newE.setAttribute("description", element.getAttributeValue("description"));
284                    }
285                    mycoreclass.addContent(newE);
286                }
287                mycoreclass.addContent(categories);
288                Document cljdom = new Document();
289                cljdom.addContent(mycoreclass);
290                out.output(cljdom, System.out);
291                MCRCategory classif = MCRXMLTransformer.getCategory(cljdom);
292                MCRClassificationBrowserData.getClassificationPool().updateClassification(classif);
293                LOGGER.debug("Classification " + cli.toString() + " successfully created!");
294                return true;
295    
296            } catch (Exception e1) {
297                LOGGER.error("Classification creation fails. Reason is:" + e1.getMessage());
298                return false;
299            }
300        }
301    
302        /**
303         * Change the description data of a classification.
304         * 
305         * @param indoc
306         *            the output from the editor dialogue
307         * @param clid
308         *            the classification ID
309         * @return true if all it's okay, else return false
310         */
311        public final boolean modifyClassificationDescription(org.jdom.Document indoc, String clid) {
312            try {
313                LOGGER.debug("Start modify classification description for " + clid);
314                Element clroot = indoc.getRootElement();
315                classif = MCRClassificationBrowserData.getClassificationPool().getClassificationAsPojo(MCRCategoryID.rootID(clid), true);
316                classif.getLabels().clear();
317    
318                @SuppressWarnings("unchecked")
319                List<Element> tagList = clroot.getChildren("label");
320                for (Element element : tagList) {
321                    MCRLabel label = new MCRLabel();
322                    label.setLang(element.getAttributeValue("lang"));
323                    label.setText(element.getAttributeValue("text"));
324                    label.setDescription(element.getAttributeValue("description"));
325                    classif.getLabels().add(label);
326                }
327    
328                MCRClassificationBrowserData.getClassificationPool().updateClassification(classif);
329                String sessionID = MCRSessionMgr.getCurrentSession().getID();
330                MCRClassificationBrowserData.ClassUserTable.put(classif.getId().getRootID(), sessionID);
331                return true;
332            } catch (Exception e1) {
333                LOGGER.error("Classification modify fails. Reason is:" + e1.getMessage());
334                return false;
335            }
336        }
337    
338        /**
339         * Move a category in a classification.
340         * 
341         * @param categid
342         *            the category ID
343         * @param clid
344         *            the classification ID
345         * @param way
346         *            the way to move
347         * @return true if all it's okay, else return false
348         */
349        public boolean moveCategoryInClassification(String categid, String clid, String way) {
350            try {
351                LOGGER.debug("Start move in classification " + clid + " the category " + categid + " in direction: " + way);
352                boolean bret = false;
353    
354                MCRCategoryID categoryId = new MCRCategoryID(clid, categid);
355                classif = MCRClassificationBrowserData.getClassificationPool().getClassificationAsPojo(MCRCategoryID.rootID(clid), true);
356    
357                if (way.equalsIgnoreCase("up")) {
358                    MCRCategory categ = findCategory(classif, categoryId);
359                    bret = moveUp(categ);
360                } else if (way.equalsIgnoreCase("down")) {
361                    MCRCategory categ = findCategory(classif, categoryId);
362                    bret = moveDown(categ);
363                } else if (way.equalsIgnoreCase("right")) {
364                    MCRCategory categ = findCategory(classif, categoryId);
365                    bret = moveRight(categ);
366                } else if (way.equalsIgnoreCase("left")) {
367                    MCRCategory categ = findCategory(classif, categoryId);
368                    bret = moveLeft(categ);
369                }
370    
371                if (bret) {
372                    MCRClassificationBrowserData.getClassificationPool().updateClassification(classif);
373                    String sessionID = MCRSessionMgr.getCurrentSession().getID();
374                    MCRClassificationBrowserData.ClassUserTable.put(classif.getId().getRootID(), sessionID);
375                    bret = true;
376                }
377                return bret;
378            } catch (Exception e1) {
379                LOGGER.error("Classification modify failed - the Reason is:" + e1.getMessage(), e1);
380                return false;
381            }
382        }
383    
384        private boolean moveUp(MCRCategory cat) {
385            MCRCategory parent = cat.getParent();
386            int index = parent.getChildren().indexOf(cat);
387            if (index > 0) {
388                parent.getChildren().remove(index);
389                parent.getChildren().add(index - 1, cat);
390                return true;
391            } else {
392                return false;
393            }
394        }
395    
396        private boolean moveDown(MCRCategory cat) {
397            MCRCategory parent = cat.getParent();
398            int index = parent.getChildren().indexOf(cat);
399            if (index < (parent.getChildren().size() - 1)) {
400                parent.getChildren().remove(index);
401                parent.getChildren().add(index + 1, cat);
402                return true;
403            } else {
404                return false;
405            }
406        }
407    
408        private boolean moveRight(MCRCategory cat) {
409            MCRCategory parent = cat.getParent();
410            if (parent.getChildren().size() == 1) {
411                return false;
412            }
413            int index = parent.getChildren().indexOf(cat);
414            parent.getChildren().remove(index);
415            if (index > 0) {
416                index--;
417            }
418            parent.getChildren().get(index).getChildren().add(cat);
419            MCRClassificationBrowserData.getClassificationPool().getMovedCategories().add(cat.getId());
420            return true;
421        }
422    
423        private boolean moveLeft(MCRCategory cat) {
424            MCRCategory parent = cat.getParent();
425    
426            if ((!parent.isCategory()) || parent == null) {
427                return false;
428            }
429    
430            MCRCategory grandParent = parent.getParent();
431            if (grandParent == null) {
432                grandParent = classif;
433            }
434    
435            int oldIndex = parent.getChildren().indexOf(cat);
436            int newIndex = grandParent.getChildren().indexOf(parent);
437            parent.getChildren().remove(oldIndex);
438    
439            grandParent.getChildren().add(newIndex + 1, cat);
440            MCRClassificationBrowserData.getClassificationPool().getMovedCategories().add(cat.getId());
441            return true;
442        }
443    
444        public boolean saveAll() {
445            final boolean saveAll = MCRClassificationBrowserData.getClassificationPool().saveAll();
446            MCRClassificationBrowserData.clearUserClassTable(MCRSessionMgr.getCurrentSession());
447            return saveAll;
448        }
449    
450        public boolean purgeAll() {
451            MCRClassificationBrowserData.clearUserClassTable(MCRSessionMgr.getCurrentSession());
452            return MCRClassificationBrowserData.getClassificationPool().purgeAll();
453        }
454    
455        /**
456         * Delete a category from a classification.
457         * 
458         * @param clid
459         *            the classification ID
460         * @param categid
461         *            the category ID
462         * @return true if all it's okay, else return false
463         */
464        public final int deleteCategoryInClassification(String clid, String categid) {
465            try {
466                LOGGER.debug("Start delete in classification " + clid + " the category: " + categid);
467                int cnt = 0;
468    
469                classif = MCRClassificationBrowserData.getClassificationPool().getClassificationAsPojo(MCRCategoryID.rootID(clid), true);
470                MCRCategoryID id = new MCRCategoryID(clid, categid);
471                MCRCategory categToDelete = findCategory(classif, id);
472                MCRCategory parent = categToDelete.getParent();
473    
474                if (parent != null) {
475                    Collection<String> links = MCRCategLinkServiceFactory.getInstance().getLinksFromCategory(id);
476                    if (links.isEmpty()) {
477                        parent.getChildren().remove(categToDelete);
478                        MCRClassificationBrowserData.getClassificationPool().updateClassification(classif);
479                        String sessionID = MCRSessionMgr.getCurrentSession().getID();
480                        MCRClassificationBrowserData.ClassUserTable.put(classif.getId().getRootID(), sessionID);
481                        cnt = links.size();
482                        LOGGER.info("Classif: " + classif.getId().getRootID());
483                    }
484                } else {
485                    LOGGER.warn("Category " + categid + " in classification: " + clid + " not found! - nothing todo");
486                }
487    
488                return cnt;
489            } catch (Exception e1) {
490                LOGGER.error("Categorie delete failed - the Reason is:" + e1.getMessage() + " " + e1.toString());
491                e1.printStackTrace();
492                return 1;
493            }
494        }
495    
496        /**
497         * Delete a classification from the system.
498         * 
499         * @param clid
500         *            the classification ID
501         * @return
502         */
503        public final boolean deleteClassification(String clid) {
504            LOGGER.debug("Start delete classification " + clid);
505            try {
506                MCRCategoryID mcrclid = MCRCategoryID.rootID(clid);
507                //only delete with DAO if Classification really exists.
508                if (DAO.exist(mcrclid)) {
509                    DAO.deleteCategory(mcrclid);
510                }
511                MCRClassificationBrowserData.getClassificationPool().deleteClassification(mcrclid);
512                LOGGER.debug("Classification: " + clid + " deleted.");
513                return true;
514            } catch (Exception e) {
515                LOGGER.error("Classification delete failed - the Reason is:" + e.getMessage() + " .");
516                return false;
517            }
518        }
519    
520        /**
521         * Here come private methods
522         */
523    
524        public MCRCategory findCategory(MCRCategory classification, MCRCategoryID ID) {
525            MCRCategory found = null;
526            for (MCRCategory cat : classification.getChildren()) {
527                if (cat.getId().equals(ID)) {
528                    found = cat;
529                    break;
530                }
531                MCRCategory rFound = findCategory(cat, ID);
532                if (rFound != null) {
533                    found = rFound;
534                    break;
535                }
536            }
537    
538            return found;
539        }
540    
541        public final void deleteTempFile() {
542            if (fout != null && fout.isFile())
543                fout.delete();
544            fout = null;
545        }
546    
547        public final String setTempFile(String name, FileItem fi) {
548            String fname = name;
549            fname.replace(' ', '_');
550            try {
551                fout = new File(CONFIG.getString("MCR.Editor.FileUpload.TempStoragePath"), fname);
552                FileOutputStream fouts = new FileOutputStream(fout);
553                MCRUtils.copyStream(fi.getInputStream(), fouts);
554                fouts.close();
555                fname = fout.getPath();
556                LOGGER.info("Classification temporary stored under " + name);
557            } catch (Exception allE) {
558                LOGGER.info("Error storing under " + fname + "  Error: " + allE.getMessage());
559                fname = null;
560            }
561            return fname;
562        }
563    
564    }