001    /*
002     * $RCSfile: MCRClassificationBrowserData.java,v $
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     * 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    package org.mycore.datamodel.classifications;
025    
026    import java.util.ArrayList;
027    import java.util.Collection;
028    import java.util.Hashtable;
029    import java.util.Iterator;
030    import java.util.List;
031    import java.util.Map;
032    
033    import org.apache.log4j.Logger;
034    import org.jdom.Document;
035    import org.jdom.Element;
036    import org.jdom.Namespace;
037    import org.jdom.xpath.XPath;
038    import org.mycore.access.MCRAccessManager;
039    import org.mycore.common.MCRConfiguration;
040    import org.mycore.common.MCRSession;
041    import org.mycore.common.MCRSessionMgr;
042    import org.mycore.common.events.MCRSessionEvent;
043    import org.mycore.common.events.MCRSessionListener;
044    import org.mycore.datamodel.classifications2.MCRCategLinkServiceFactory;
045    import org.mycore.datamodel.classifications2.MCRCategory;
046    import org.mycore.datamodel.classifications2.MCRCategoryID;
047    import org.mycore.datamodel.classifications2.MCRLabel;
048    
049    /**
050     * Instances of MCRClassificationBrowser contain the data of the currently
051     * displayed navigation tree. MCRClassificationBrowser uses one
052     * MCRClassificationBrowserData instance per browser session to store and update
053     * the category lines to be displayed.
054     * 
055     * @author Anja Schaar
056     * 
057     */
058    public class MCRClassificationBrowserData {
059    
060        protected String pageName;
061    
062        protected String xslStyle;
063    
064        protected String uri;
065    
066        private static MCRConfiguration config;
067    
068        private static final Logger LOGGER = Logger.getLogger(MCRClassificationBrowserData.class);
069    
070        private ArrayList<Element> lines;
071    
072        private MCRCategory classif;
073    
074        private String startPath = "";
075    
076        private String actItemID = "";
077    
078        private String lastItemID = "";
079    
080        private String[] categFields;
081    
082        private String emptyLeafs = null;
083    
084        private String view = null;
085    
086        private boolean comments = false;
087    
088        private String searchField = "";
089    
090        private boolean sort = false;
091    
092        private String objectType = null;
093    
094        private String[] objectTypeArray = null;
095    
096        private String restriction = null;
097    
098        int maxlevel = 0;
099    
100        int totalNumOfDocs = 0;
101    
102        public static Map<String, String> ClassUserTable = new Hashtable<String, String>();
103    
104        private static MCRSessionListener ClassUserTableCleaner = new MCRSessionListener() {
105    
106            public void sessionEvent(MCRSessionEvent event) {
107                switch (event.getType()) {
108                case destroyed:
109                    clearUserClassTable(event.getSession());
110                    break;
111                default:
112                    LOGGER.debug("Skipping event: " + event.getType());
113                    break;
114                }
115            }
116    
117        };
118    
119        static {
120            MCRSessionMgr.addSessionListener(ClassUserTableCleaner);
121        }
122    
123        public MCRClassificationBrowserData(final String u, final String mode, final String actclid, final String actEditorCategid)
124                throws Exception {
125            uri = u;
126            config = MCRConfiguration.instance();
127            LOGGER.debug(" incomming Path " + uri);
128    
129            final String[] uriParts = uri.split("/"); // mySplit();
130            LOGGER.info(" Start");
131            String classifID = null;
132            final String browserClass = (uriParts.length <= 1 ? "default" : uriParts[1]);
133            LOGGER.debug(" PathParts - classification " + browserClass);
134            LOGGER.debug(" Number of PathParts =" + uriParts.length);
135            try {
136                classifID = config.getString("MCR.ClassificationBrowser." + browserClass + ".Classification");
137            } catch (final org.mycore.common.MCRConfigurationException noClass) {
138                classifID = actclid;
139            }
140            try {
141                pageName = config.getString("MCR.ClassificationBrowser." + browserClass + ".EmbeddingPage");
142            } catch (final org.mycore.common.MCRConfigurationException noPagename) {
143                pageName = config.getString("MCR.ClassificationBrowser.default.EmbeddingPage");
144            }
145            try {
146                xslStyle = config.getString("MCR.ClassificationBrowser." + browserClass + ".Style");
147            } catch (final org.mycore.common.MCRConfigurationException noStyle) {
148                xslStyle = config.getString("MCR.ClassificationBrowser.default.Style");
149            }
150            try {
151                searchField = config.getString("MCR.ClassificationBrowser." + browserClass + ".searchField");
152            } catch (final org.mycore.common.MCRConfigurationException noSearchfield) {
153                searchField = config.getString("MCR.ClassificationBrowser.default.searchField");
154            }
155    
156            try {
157                emptyLeafs = config.getString("MCR.ClassificationBrowser." + browserClass + ".EmptyLeafs");
158            } catch (final org.mycore.common.MCRConfigurationException noEmptyLeafs) {
159                emptyLeafs = config.getString("MCR.ClassificationBrowser.default.EmptyLeafs");
160            }
161            try {
162                view = config.getString("MCR.ClassificationBrowser." + browserClass + ".View");
163            } catch (final org.mycore.common.MCRConfigurationException noView) {
164                view = config.getString("MCR.ClassificationBrowser.default.View");
165            }
166            setObjectTypes(browserClass);
167    
168            sort = config.getBoolean("MCR.ClassificationBrowser." + browserClass + ".Sort", false);
169            comments = config.getBoolean("MCR.ClassificationBrowser." + browserClass + ".Comments", false);
170            restriction = config.getString("MCR.ClassificationBrowser." + browserClass + ".Restriction", null);
171    
172            startPath = browserClass;
173    
174            if ("edit".equals(mode)) {
175                pageName = config.getString("MCR.classeditor.EmbeddingPage");
176                xslStyle = config.getString("MCR.classeditor.Style");
177                sort = false;
178                view = "tree";
179    
180                if (classifID.length() == 0) {
181                    return;
182                }
183            }
184    
185            if (emptyLeafs == null) {
186                emptyLeafs = "yes";
187            }
188            if (view == null || !view.endsWith("flat")) {
189                view = "tree";
190            }
191            LOGGER.info("uriParts length: " + uriParts.length);
192            clearPath(uriParts);
193            MCRCategoryID id = MCRCategoryID.rootID(classifID);
194            setClassification(id);
195            setActualPath(actEditorCategid);
196    
197            if (LOGGER.isDebugEnabled()) {
198                LOGGER.debug(" SetClassification " + classifID);
199                LOGGER.debug(" Empty nodes: " + emptyLeafs);
200                LOGGER.debug(" View: " + view);
201                LOGGER.debug(" Comment: " + comments);
202                LOGGER.debug(" Doctypes: " + objectType);
203                for (String element : objectTypeArray) {
204                    LOGGER.debug(" Type: " + element);
205                }
206                LOGGER.debug(" Restriction: " + restriction);
207                LOGGER.debug(" Sort: " + sort);
208            }
209        }
210    
211        private void setObjectTypes(final String browserClass) {
212            LOGGER.debug("setObjectTypes(" + browserClass + ")");
213            try {
214                // NOTE: read *.Doctype for compatiblity reasons
215                objectType = config.getString("MCR.ClassificationBrowser." + browserClass + ".Objecttype", config.getString(
216                        "MCR.ClassificationBrowser." + browserClass + ".Doctype", null));
217            } catch (final org.mycore.common.MCRConfigurationException noDoctype) {
218                objectType = config.getString("MCR.ClassificationBrowser.default.ObjectType", config
219                        .getString("MCR.ClassificationBrowser.default.Doctype"));
220            }
221    
222            if (objectType != null) {
223                objectTypeArray = objectType.split(",");
224            } else {
225                objectTypeArray = new String[0];
226            }
227        }
228    
229        public String getUri() {
230            return uri;
231        }
232    
233        /**
234         * Returns true if category comments for the classification currently
235         * displayed should be shown.
236         */
237        public boolean showComments() {
238            return comments;
239        }
240    
241        /**
242         * Returns the pageName for the classification
243         */
244        public String getPageName() {
245            return pageName;
246        }
247    
248        /**
249         * Returns the xslStyle for the classification
250         */
251        public String getXslStyle() {
252            return xslStyle;
253        }
254    
255        public MCRCategory getClassification() {
256            return classif;
257        }
258    
259        @SuppressWarnings("unchecked")
260        public org.jdom.Document loadTreeIntoSite(final org.jdom.Document cover, final org.jdom.Document browser) {
261            final Element placeholder = cover.getRootElement().getChild("classificationBrowser");
262            LOGGER.info(" Found Entry at " + placeholder);
263            if (placeholder != null) {
264                final List<Element> children = browser.getRootElement().getChildren();
265                for (Element child : children) {
266                    placeholder.addContent((Element) child.clone());
267                }
268            }
269            LOGGER.debug(cover);
270            return cover;
271        }
272    
273        public final void setClassification(final MCRCategoryID classifID) throws Exception {
274            classif = getClassificationPool().getClassificationAsPojo(classifID, true);
275            if (classif == null)
276                return;
277            lines = new ArrayList<Element>();
278            totalNumOfDocs = 0;
279            putCategoriesintoLines(-1, classif.getChildren());
280            LOGGER.debug("Arraylist of CategItems initialized - Size " + lines.size());
281        }
282    
283        private void clearPath(final String[] uriParts) throws Exception {
284            final String[] cati = new String[uriParts.length];
285            String path = "";
286            if (uriParts.length == 1) {
287                path = "/" + uriParts[0];
288            } else {
289                path = "/" + uriParts[1];
290            }
291            int len = 0;
292            // pfad bereinigen
293            for (int k = 2; k < uriParts.length; k++) {
294                LOGGER.debug(" uriParts[k]=" + uriParts[k] + " k=" + k);
295                if (uriParts[k].length() > 0) {
296                    if (uriParts[k].equalsIgnoreCase("..") && len > 0) {
297                        len--;
298                    } else {
299                        cati[len] = uriParts[k];
300                        len++;
301                    }
302                }
303            }
304    
305            // remove double entries from path
306            // (if an entry appears the 2nd time it will not be displayed -> so we
307            // can remove it here)
308            final ArrayList<String> result = new ArrayList<String>();
309            for (int i = 0; i < len; i++) {
310                final String x = cati[i];
311                if (result.contains(x)) {
312                    result.remove(x);
313                } else {
314                    result.add(x);
315                }
316            }
317    
318            // reinitialisieren
319            categFields = new String[result.size()];
320            int j = 0;
321            for (String uriPart : result) {
322                categFields[j] = uriPart;
323                j++;
324                path += "/" + uriPart;
325            }
326            this.uri = path;
327        }
328    
329        private void setActualPath(final String actEditorCategid) throws Exception {
330            actItemID = lastItemID = "";
331    
332            for (String categID : categFields) {
333                update(categID);
334                lastItemID = actItemID;
335                actItemID = categID;
336            }
337            if (actEditorCategid != null) {
338                actItemID = lastItemID = actEditorCategid;
339            }
340        }
341    
342        private Element setTreeline(MCRCategory categ) {
343            Element categElement = MCRCategoryElementFactory.getCategoryElement(categ, false, 0);
344            categElement.setAttribute("level", String.valueOf(categ.getLevel() + 1));
345    
346            if (categ.hasChildren()) {
347                categElement.setAttribute("hasChildren", "T");
348            } else {
349                categElement.setAttribute("hasChildren", " ");
350            }
351            return categElement;
352        }
353    
354        private Element getTreeline(final int i) {
355            if (i >= lines.size()) {
356                return null;
357            }
358            return lines.get(i);
359        }
360    
361        private void putCategoriesintoLines(final int startpos, final List<MCRCategory> children) {
362            LOGGER.debug("Start Explore Arraylist of CategItems  ");
363            int i = startpos;
364            List<MCRCategoryID> ids = new ArrayList<MCRCategoryID>();
365    
366            for (MCRCategory cat : children) {
367                ids.add(cat.getId());
368            }
369    
370            for (MCRCategory cat : children) {
371                lines.add(++i, setTreeline(cat));
372            }
373            LOGGER.debug("End Explore - Arraylist of CategItems ");
374        }
375    
376        public org.jdom.Document createXmlTreeforAllClassifications() throws Exception {
377            LOGGER.debug("create XML tree for all classifications");
378            final Element xDocument = new Element("classificationbrowse");
379            final Element CreateClassButton = new Element("userCanCreate");
380            if (MCRAccessManager.checkPermission("create-classification")) {
381                CreateClassButton.addContent("true");
382            } else {
383                CreateClassButton.addContent("false");
384            }
385    
386            xDocument.addContent(CreateClassButton);
387    
388            final Element xNavtree = new Element("classificationlist");
389            xDocument.addContent(xNavtree);
390            String browserClass = "";
391    
392            LOGGER.debug("query classification links");
393            MCRClassificationPool classificationPool = getClassificationPool();
394            for (MCRCategoryID classID : classificationPool.getAllIDs()) {
395                MCRCategory classif = classificationPool.getClassificationAsPojo(classID, false);
396                LOGGER.debug("get classification " + classID);
397                LOGGER.debug("get browse element");
398                Element cli = getBrowseElement(classif);
399                LOGGER.debug("get browse element ... done");
400                String sessionID = MCRSessionMgr.getCurrentSession().getID();
401                // set browser type
402                try {
403                    browserClass = config.getString("MCR.classeditor." + classif.getId().getRootID());
404                } catch (final Exception ignore) {
405                    browserClass = "default";
406                }
407                // set permissions
408                if (classificationPool.isEdited(classID)) {
409                    cli.setAttribute("edited", "true");
410                    cli.setAttribute("userCanEdit", "false");
411                    cli.setAttribute("userCanDelete", "false");
412                } else {
413                    cli.setAttribute("edited", "false");
414                    if (MCRAccessManager.checkPermission(classID.getRootID(), "writedb")) {
415                        cli.setAttribute("userCanEdit", "true");
416                    } else {
417                        cli.setAttribute("userCanEdit", "false");
418                    }
419                    final boolean mayDelete = MCRAccessManager.checkPermission(classID.getRootID(), "deletedb");
420                    if (mayDelete) {
421                        cli.setAttribute("userCanDelete", "true");
422                        LOGGER.debug("counting linked objects");
423                        boolean hasLinks = MCRCategLinkServiceFactory.getInstance().hasLinks(classif).get(classID).booleanValue();
424                        LOGGER.debug("counting linked objects ... done");
425                        cli.setAttribute("hasLinks", String.valueOf(hasLinks));
426                    } else {
427                        cli.setAttribute("userCanDelete", "false");
428                    }
429                }
430                // set done flag
431                if (ClassUserTable.containsKey(classID.getRootID())) {
432                    if (ClassUserTable.get(classID.getRootID()) != sessionID) {
433                        MCRSession oldsession = MCRSessionMgr.getSession(ClassUserTable.get(classID.getRootID()));
434                        if (null != oldsession)
435                            cli.setAttribute("userEdited", oldsession.getCurrentUserID());
436                        else {
437                            ClassUserTable.remove(classID.getRootID());
438                            cli.setAttribute("userEdited", "false");
439                        }
440                    } else {
441                        cli.setAttribute("userEdited", "false");
442                    }
443                } else {
444                    cli.setAttribute("userEdited", "false");
445                }
446    
447                cli.setAttribute("browserClass", browserClass);
448                setObjectTypes(browserClass);
449                xNavtree.addContent(cli);
450            }
451            return new Document(xDocument);
452        }
453    
454        private static Element getBrowseElement(MCRCategory classif) {
455            Element ce = new Element("classification");
456            ce.setAttribute("ID", classif.getId().getRootID());
457            for (MCRLabel label : classif.getLabels()) {
458                Element labelElement = new Element("label");
459                if (label.getLang() != null) {
460                    labelElement.setAttribute("lang", label.getLang(), Namespace.XML_NAMESPACE);
461                }
462                if (label.getText() != null) {
463                    labelElement.setAttribute("text", label.getText());
464                }
465                if (label.getDescription() != null) {
466                    labelElement.setAttribute("description", label.getDescription());
467                }
468                ce.addContent(labelElement);
469            }
470            return ce;
471        }
472    
473        /**
474         * Creates an XML representation of MCRClassificationBrowserData
475         * 
476         * @author Anja Schaar
477         * 
478         */
479    
480        public org.jdom.Document createXmlTree(final String lang) throws Exception {
481    
482            LOGGER.debug("Show tree for classification:" + classif.getId());
483            final MCRCategory cl = getClassificationPool().getClassificationAsPojo(classif.getId(), true);
484            LOGGER.debug("Got classification");
485            MCRClassificationPool cp = getClassificationPool();
486            MCRLabel labels = getLabel(cl, lang);
487            Element xDocument = new Element("classificationBrowse");
488    
489            final Element xID = new Element("classifID");
490            xID.addContent(cl.getId().getRootID());
491            xDocument.addContent(xID);
492    
493            final Element xUserEdited = new Element("userEdited");
494            if (ClassUserTable.containsKey(cl.getId().getRootID())) {
495                xUserEdited.addContent(MCRSessionMgr.getSession(ClassUserTable.get(cl.getId().getRootID())).getCurrentUserID());
496            } else {
497                xUserEdited.addContent("false");
498            }
499            xDocument.addContent(xUserEdited);
500    
501            final Element xSessionID = new Element("session");
502            if (ClassUserTable.containsKey(cl.getId().getRootID())) {
503                xSessionID.addContent(ClassUserTable.get(cl.getId().getRootID()));
504            } else {
505                xSessionID.addContent("");
506            }
507    
508            xDocument.addContent(xSessionID);
509    
510            final Element xCurrentSessionID = new Element("currentSession");
511            xCurrentSessionID.addContent(MCRSessionMgr.getCurrentSession().getID());
512            xDocument.addContent(xCurrentSessionID);
513    
514            final Element xLabel = new Element("label");
515            xLabel.addContent(labels.getText());
516            xDocument.addContent(xLabel);
517    
518            final Element xDesc = new Element("description");
519            xDesc.addContent(labels.getDescription());
520            xDocument.addContent(xDesc);
521    
522            final Element xDocuments = new Element("cntDocuments");
523            xDocuments.addContent(String.valueOf(totalNumOfDocs));
524            xDocument.addContent(xDocuments);
525    
526            final Element xShowComments = new Element("showComments");
527            xShowComments.addContent(String.valueOf(showComments()));
528            xDocument.addContent(xShowComments);
529    
530            final Element xUri = new Element("uri");
531            xUri.addContent(uri);
532            xDocument.addContent(xUri);
533    
534            final Element xStartPath = new Element("startPath");
535            xStartPath.addContent(startPath);
536            xDocument.addContent(xStartPath);
537    
538            final Element xSearchField = new Element("searchField");
539            xSearchField.addContent(searchField);
540            xDocument.addContent(xSearchField);
541    
542            // add edit button if user has permission
543            final Element CreateButton = new Element("userCanCreate");
544            final Element EditButton = new Element("userCanEdit");
545            final Element DeleteButton = new Element("userCanDelete");
546            LOGGER.debug("now we check this right for the current user");
547            // now we check this right for the current user
548            if (cp.isEdited(getClassification().getId()) == false) {
549                String permString = String.valueOf(MCRAccessManager.checkPermission("create-classification"));
550                CreateButton.addContent(permString);
551                xDocument.addContent(CreateButton);
552                permString = String.valueOf(MCRAccessManager.checkPermission(cl.getId().getRootID(), "writedb"));
553                EditButton.addContent(permString);
554                xDocument.addContent(EditButton);
555                permString = String.valueOf(MCRAccessManager.checkPermission(cl.getId().getRootID(), "deletedb"));
556                DeleteButton.addContent(permString);
557                xDocument.addContent(DeleteButton);
558            } else {
559                String permString = "true";
560                CreateButton.addContent(permString);
561                xDocument.addContent(CreateButton);
562                EditButton.addContent(permString);
563                xDocument.addContent(EditButton);
564                DeleteButton.addContent(permString);
565                xDocument.addContent(DeleteButton);
566            }
567    
568            // data as XML from outputNavigationTree
569            final Element xNavtree = new Element("navigationtree");
570            xNavtree.setAttribute("classifID", cl.getId().getRootID());
571            xNavtree.setAttribute("categID", actItemID);
572            xNavtree.setAttribute("predecessor", lastItemID);
573            xNavtree.setAttribute("emptyLeafs", emptyLeafs);
574            xNavtree.setAttribute("view", view);
575            final StringBuffer sb = new StringBuffer();
576            if (objectTypeArray.length > 1) {
577                sb.append("(");
578            }
579            for (int i = 0; i < objectTypeArray.length; i++) {
580                sb.append("(objectType+=+").append(objectTypeArray[i]).append(")");
581                if ((objectTypeArray.length > 1) && (i < objectTypeArray.length - 1)) {
582                    sb.append("+or+");
583                }
584            }
585            if (objectTypeArray.length > 1) {
586                sb.append(")");
587            }
588            xNavtree.setAttribute("doctype", sb.toString());
589            xNavtree.setAttribute("restriction", restriction != null ? restriction : "");
590            xNavtree.setAttribute("searchField", searchField);
591    
592            int i = 0;
593            Element line;
594            List<MCRCategoryID> ids = new ArrayList<MCRCategoryID>();
595            LOGGER.debug("process tree lines: fetch ids");
596            while ((line = getTreeline(i++)) != null) {
597                final String catid = line.getAttributeValue("ID");
598                ids.add(new MCRCategoryID(cl.getId().getRootID(), catid));
599            }
600            i = 0;
601            LOGGER.debug("fetch Map<MCRCategoryID,Boolean>");
602            Map<MCRCategoryID, Boolean> countMap = MCRCategLinkServiceFactory.getInstance().hasLinks(getClassification());
603            LOGGER.debug("process tree lines: build xml tree");
604            while ((line = getTreeline(i++)) != null) {
605    
606                final String catid = line.getAttributeValue("ID");
607                final Boolean hasLinkValue = countMap.get(new MCRCategoryID(cl.getId().getRootID(), catid));
608                //new categories are not yet in countMap
609                boolean hasLinks = hasLinkValue != null ? hasLinkValue.booleanValue() : false;
610                final String status = line.getAttributeValue("hasChildren");
611    
612                Element label = (Element) XPath.selectSingleNode(line, "label[lang('" + lang + "')]");
613                if (label == null) {
614                    label = (Element) XPath.selectSingleNode(line, "label[lang('"
615                            + MCRConfiguration.instance().getString("MCR.Metadata.DefaultLang", "en") + "')]");
616                }
617                if (label == null) {
618                    label = (Element) XPath.selectSingleNode(line, "label");
619                }
620                final String text = label.getAttributeValue("text");
621                final String description = label.getAttributeValue("description");
622    
623                final int level = Integer.parseInt(line.getAttributeValue("level"));
624    
625                if (emptyLeafs.endsWith("no") && !hasLinks) {
626                    LOGGER.debug(" empty Leaf continue - " + emptyLeafs);
627                    continue;
628                }
629                final Element xRow = new Element("row");
630                final Element xCol1 = new Element("col");
631                final Element xCol2 = new Element("col");
632    
633                xRow.addContent(xCol1);
634                xRow.addContent(xCol2);
635                xNavtree.addContent(xRow);
636    
637                xCol1.setAttribute("lineLevel", String.valueOf(level - 1));
638                xCol1.setAttribute("childpos", "middle");
639    
640                if (level > maxlevel) {
641                    xCol1.setAttribute("childpos", "first");
642                    maxlevel = level;
643                    if (getTreeline(i) == null) {
644                        // Spezialfall nur genau ein Element
645                        xCol1.setAttribute("childpos", "firstlast");
646                    }
647                } else if (getTreeline(i) == null) {
648                    xCol1.setAttribute("childpos", "last");
649                }
650    
651                xCol1.setAttribute("folder1", "folder_plain");
652                xCol1.setAttribute("folder2", hasLinks ? "folder_closed_in_use" : "folder_closed_empty");
653    
654                if (status.equals("T")) {
655                    xCol1.setAttribute("plusminusbase", catid);
656                    xCol1.setAttribute("folder1", "folder_plus");
657                } else if (status.equals("F")) {
658                    xCol1.setAttribute("plusminusbase", catid);
659                    xCol1.setAttribute("folder1", "folder_minus");
660                    xCol1.setAttribute("folder2", hasLinks ? "folder_open_in_use" : "folder_open_empty");
661                }
662    
663                String search = uri;
664                search += "/" + catid;
665    
666                if (search.indexOf("//") > 0)
667                    search = search.substring(0, search.indexOf("//")) + search.substring(search.indexOf("//") + 1);
668    
669                xCol2.setAttribute("searchbase", search);
670                xCol2.setAttribute("lineID", catid);
671                xCol2.setAttribute("hasLinks", String.valueOf(hasLinks));
672    
673                xCol2.addContent(text);
674    
675                if (showComments() && (description != null)) {
676                    final Element comment = new Element("comment");
677                    xCol2.addContent(comment);
678                    comment.setText(description);
679                }
680            }
681            LOGGER.debug("Building XML document");
682    
683            xNavtree.setAttribute("rowcount", "" + i);
684            xDocument.addContent(xNavtree);
685    
686            if ("true".equals(sort)) {
687                xDocument = sortMyTree(xDocument);
688            }
689            Document doc = new org.jdom.Document(xDocument);
690            //MCRUtils.writeJDOMToSysout(doc);
691            return doc;
692        }
693    
694        public void update(final String categID) throws Exception {
695            int lastLevel = 0;
696            boolean hideLevel = false;
697    
698            MCRCategory parent = MCRClassificationBrowserData.getClassificationPool().getClassificationAsPojo(classif.getId(), true);
699            MCRCategory cat = findCategory(parent, categID);
700    
701            LOGGER.debug(" update CategoryTree for: " + categID);
702            Element line;
703            for (int i = 0; i < lines.size(); i++) {
704                line = getTreeline(i);
705                final String catid = line.getAttributeValue("ID");
706                final String status = line.getAttributeValue("hasChildren");
707                final int level = Integer.parseInt(line.getAttributeValue("level"));
708    
709                hideLevel = hideLevel && (level > lastLevel);
710                LOGGER.debug(" compare CategoryTree on " + i + "_" + catid + " to " + categID);
711                if (view.endsWith("tree")) {
712                    if (hideLevel) {
713                        lines.remove(i--);
714                    } else if (categID.equals(catid)) {
715                        if (status.equals("F")) // hide expanded category - //
716                        // children
717                        {
718                            line.setAttribute("hasChildren", "T");
719                            hideLevel = true;
720                            lastLevel = level;
721                        } else if (status.equals("T")) // expand category - //
722                        // children
723                        {
724                            line.setAttribute("hasChildren", "F");
725                            putCategoriesintoLines(i, cat.getChildren());
726                        }
727                    }
728                } else {
729                    if (categID.equalsIgnoreCase(catid)) {
730                        line.setAttribute("level", "0");
731                        LOGGER.info(" expand " + catid);
732                        line.setAttribute("hasChildren", "F");
733                        putCategoriesintoLines(i, cat.getChildren());
734                    } else {
735                        LOGGER.debug(" remove lines " + i + "_" + catid);
736                        lines.remove(i--);
737                    }
738                }
739    
740            }
741        }
742    
743        // don't use it works not really good
744    
745        private final Element sortMyTree(final Element xDocument) {
746            Element xDoc = (Element) xDocument.clone();
747            final Element navitree = ((Element) xDoc.getChild("navigationtree"));
748            // separate
749            ArrayList<String> itemname = new ArrayList<String>();
750            ArrayList<Integer> itemlevel = new ArrayList<Integer>();
751            ArrayList<Element> itemelm = new ArrayList<Element>();
752            @SuppressWarnings("unchecked")
753            List<Element> navitreelist = navitree.getChildren();
754            for (int i = 0; i < navitreelist.size(); i++) {
755                final Element child = navitreelist.get(i);
756                final Element col1 = (Element) (child.getChildren().get(0));
757                final Element col2 = (Element) (child.getChildren().get(1));
758                final String sText = col2.getText();
759                int level = 0;
760                try {
761                    level = col1.getAttribute("lineLevel").getIntValue();
762                } catch (final Exception ignored) {
763                }
764                itemname.add(sText);
765                itemlevel.add(new Integer(level));
766                itemelm.add((Element) child.clone());
767                navitree.removeContent(child);
768                i--;
769            }
770            int[] itemnum = new int[itemname.size()];
771            for (int i = 0; i < itemname.size(); i++) {
772                itemnum[i] = i;
773            }
774            // sort
775            sortMyTreePerLevel(0, itemname.size(), 1, itemname, itemlevel, itemnum);
776            // write back
777            for (int i = 0; i < itemnum.length; i++) {
778                navitree.addContent(itemelm.get(itemnum[i]));
779            }
780            return xDoc;
781        }
782    
783        private final void sortMyTreePerLevel(int von, int bis, int level, ArrayList<String> itemname, ArrayList<Integer> itemlevel,
784                int[] itemnum) {
785            if (von == bis)
786                return;
787            // System.out.println("$$$>" + von + " " + bis);
788            for (int i = von; i < bis - 1; i++) {
789                // System.out.println("III>" + i + " " + itemname.get(itemnum[i]));
790                if (itemlevel.get(itemnum[i]) != level) {
791                    // System.out.println("%%%> inner sort");
792                    int start = i;
793                    int stop = start;
794                    while (stop + 1 < bis && itemlevel.get(itemnum[stop + 1]).intValue() > level) {
795                        stop++;
796                    }
797                    // System.out.println("--------------------");
798                    sortMyTreePerLevel(start, stop + 1, level + 2, itemname, itemlevel, itemnum);
799                    // System.out.println("--------------------");
800                    i = stop + 1;
801                    if (i >= bis)
802                        continue;
803                }
804                String aktitem = itemname.get(itemnum[i]);
805                for (int j = i + 1; j < bis; j++) {
806                    if (level != itemlevel.get(itemnum[j]).intValue())
807                        continue;
808                    // System.out.println("%%%>" + i + " " + aktitem + " " + j + " "
809                    // + itemname.get(itemnum[j]) + " " + level + " " +
810                    // itemlevel.get(itemnum[j]).intValue());
811                    if (aktitem.compareTo(itemname.get(itemnum[j])) > 0) {
812                        // must switch
813                        // System.out.println("%%%> swap " + i + " with " + j);
814                        int[] swap = itemnum.clone();
815                        itemnum[i] = itemnum[j];
816                        int ii = i;
817                        for (int jj = j; jj < bis; jj++) {
818                            // System.out.println("* " + ii + " " + jj);
819                            itemnum[ii] = swap[jj];
820                            ii++;
821                        }
822                        for (int jj = i; jj < j; jj++) {
823                            // System.out.println("# " + ii + " " + jj);
824                            itemnum[ii] = swap[jj];
825                            ii++;
826                        }
827                        j = i;
828                        aktitem = itemname.get(itemnum[i]);
829                    }
830                }
831            }
832        }
833    
834        /**
835         * @return Returns the pool.
836         */
837        public static MCRClassificationPool getClassificationPool() {
838            MCRSession session = MCRSessionMgr.getCurrentSession();
839            Object cp = session.get("MCRClassificationPool.instance");
840            if (cp != null && cp instanceof MCRClassificationPool) {
841                return (MCRClassificationPool) cp;
842            }
843            MCRClassificationPool classPool = new MCRClassificationPool();
844            session.put("MCRClassificationPool.instance", classPool);
845            return classPool;
846        }
847    
848        private static MCRLabel getLabel(MCRCategory co, String lang) {
849            for (MCRLabel label : co.getLabels()) {
850                if (label.getLang().equals(lang)) {
851                    return label;
852                }
853            }
854            return new MCRLabel();
855        }
856    
857        public static void clearUserClassTable(MCRSession session) {
858            final String curSessionID = session.getID();
859            final Iterator<Map.Entry<String, String>> it = ClassUserTable.entrySet().iterator();
860            while (it.hasNext()) {
861                Map.Entry<String, String> entry = it.next();
862                if (entry.getValue().equals(curSessionID)) {
863                    LOGGER.info("Release classification " + entry.getKey() + " lock.");
864                    it.remove();
865                }
866            }
867        }
868    
869        private MCRCategory findCategory(MCRCategory parent, String id) {
870            MCRCategory found = null;
871            for (MCRCategory cat : parent.getChildren()) {
872                if (cat.getId().getID().equals(id)) {
873                    found = cat;
874                    LOGGER.debug("Found Category: " + found.getId().getID());
875                    break;
876                }
877                MCRCategory rFound = findCategory(cat, id);
878                if (rFound != null) {
879                    found = rFound;
880                    break;
881                }
882            }
883    
884            return found;
885        }
886    
887        private static class MCRCategoryElementFactory {
888            static Element getCategoryElement(MCRCategory category, boolean withCounter, int numberObjects) {
889                Element ce = new Element("category");
890                Collection<MCRLabel> labels = category.getLabels();
891                ce.setAttribute("ID", category.getId().getID());
892                if (withCounter) {
893                    ce.setAttribute("counter", Integer.toString(numberObjects));
894                }
895    
896                for (MCRLabel label : labels) {
897                    ce.addContent(getElement(label));
898                }
899                for (MCRCategory child : category.getChildren()) {
900                    ce.addContent(getCategoryElement(child, withCounter, numberObjects));
901                }
902                return ce;
903            }
904    
905            private static Element getElement(MCRLabel label) {
906                Element le = new Element("label");
907                if (stringNotEmpty(label.getLang())) {
908                    le.setAttribute("lang", label.getLang(), Namespace.XML_NAMESPACE);
909                }
910                if (stringNotEmpty(label.getText())) {
911                    le.setAttribute("text", label.getText());
912                }
913                if (stringNotEmpty(label.getDescription())) {
914                    le.setAttribute("description", label.getDescription());
915                }
916                return le;
917            }
918    
919            private static boolean stringNotEmpty(String test) {
920                if (test != null && test.length() > 0) {
921                    return true;
922                }
923                return false;
924            }
925        }
926    
927    }