001 /*
002 *
003 * $Revision: 15374 $ $Date: 2009-06-15 17:23:48 +0200 (Mon, 15 Jun 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.classifications2.utils;
025
026 import static org.jdom.Namespace.XML_NAMESPACE;
027 import static org.mycore.common.MCRConstants.XLINK_NAMESPACE;
028 import static org.mycore.common.MCRConstants.XSI_NAMESPACE;
029
030 import java.net.URI;
031 import java.util.Arrays;
032 import java.util.Comparator;
033 import java.util.Iterator;
034 import java.util.List;
035 import java.util.Map;
036 import java.util.regex.Matcher;
037 import java.util.regex.Pattern;
038
039 import org.jdom.Document;
040 import org.jdom.Element;
041 import org.mycore.datamodel.classifications2.MCRCategLinkServiceFactory;
042 import org.mycore.datamodel.classifications2.MCRCategory;
043 import org.mycore.datamodel.classifications2.MCRCategoryID;
044 import org.mycore.datamodel.classifications2.MCRLabel;
045
046 /**
047 *
048 * @author Thomas Scheffler (yagee)
049 *
050 * @version $Revision: 15374 $ $Date: 2008-02-06 17:27:24 +0000 (Mi, 06 Feb
051 * 2008) $
052 */
053 public class MCRCategoryTransformer {
054
055 private static final String STANDARD_LABEL = "{text}";
056
057 /**
058 * transforms a <code>MCRCategory</code> into a JDOM Document.
059 *
060 * The Document will have a root tag with name "mycoreclass".
061 *
062 * @param cl
063 * Classification
064 * @return
065 */
066 public static Document getMetaDataDocument(MCRCategory cl, boolean withCounter) {
067 Map<MCRCategoryID, Number> countMap = null;
068 if (withCounter) {
069 countMap = MCRCategLinkServiceFactory.getInstance().countLinks(cl, false);
070 }
071 return MetaDataElementFactory.getDocument(cl, countMap);
072 }
073
074 /**
075 * transforms a <code>MCRCategory</code> into a JDOM Element.
076 *
077 * The element will have the tag name "category".
078 *
079 * @param category
080 * a category of a classification
081 * @return
082 */
083 public static Element getMetaDataElement(MCRCategory category, boolean withCounter) {
084 Map<MCRCategoryID, Number> countMap = null;
085 if (withCounter) {
086 countMap = MCRCategLinkServiceFactory.getInstance().countLinks(category, false);
087 }
088 return MetaDataElementFactory.getElement(category, countMap);
089 }
090
091 /**
092 * transforms a <code>Classification</code> into a MCR Editor definition (
093 * <code><items></code>).
094 *
095 * @param cl
096 * Classification
097 * @param sort
098 * if true, sort items
099 * @param emptyLeaves
100 * if true, also include empty leaves
101 * @return
102 */
103 public static Element getEditorItems(MCRCategory cl, boolean sort, boolean emptyLeaves) {
104 return new ItemElementFactory(cl, STANDARD_LABEL, sort, emptyLeaves).getResult();
105 }
106
107 /**
108 * transforms a <code>Classification</code> into a MCR Editor definition (<code><items></code>).
109 *
110 * This method allows you to specify how the labels will look like.
111 * <code>labelFormat</code> is simply a String that is parsed for a few
112 * key words, that will be replaced by a dynamic value. The following
113 * keywords can be used at the moment:
114 * <ul>
115 * <li>{id}</li>
116 * <li>{text}</li>
117 * <li>{description}</li>
118 * <li>{count}</li>
119 * </ul>
120 *
121 * @param cl
122 * Classification
123 * @param labelFormat
124 * format String as specified above
125 * @param sort
126 * if true, sort items
127 * @param emptyLeaves
128 * if true, also include empty leaves
129 * @return
130 */
131 public static Element getEditorItems(MCRCategory cl, String labelFormat, boolean sort, boolean emptyLeaves) {
132 return new ItemElementFactory(cl, labelFormat, sort, emptyLeaves).getResult();
133 }
134
135 private static class MetaDataElementFactory {
136 static Document getDocument(MCRCategory cl, Map<MCRCategoryID, Number> countMap) {
137 Document cd = new Document(new Element("mycoreclass"));
138 cd.getRootElement().setAttribute("noNamespaceSchemaLocation", "MCRClassification.xsd", XSI_NAMESPACE);
139 cd.getRootElement().setAttribute("ID", cl.getId().getRootID());
140 cd.getRootElement().addNamespaceDeclaration(XLINK_NAMESPACE);
141 MCRCategory root = cl.isClassification() ? cl : cl.getRoot();
142 for (MCRLabel label : root.getLabels()) {
143 cd.getRootElement().addContent(getElement(label));
144 }
145 Element categories = new Element("categories");
146 cd.getRootElement().addContent(categories);
147 if (cl.isClassification()) {
148 for (MCRCategory category : cl.getChildren()) {
149 categories.addContent(getElement(category, countMap));
150 }
151 } else {
152 categories.addContent(getElement(cl, countMap));
153 }
154 return cd;
155 }
156
157 static Element getElement(MCRLabel label) {
158 Element le = new Element("label");
159 if (stringNotEmpty(label.getLang())) {
160 le.setAttribute("lang", label.getLang(), XML_NAMESPACE);
161 }
162 if (stringNotEmpty(label.getText())) {
163 le.setAttribute("text", label.getText());
164 }
165 if (stringNotEmpty(label.getDescription())) {
166 le.setAttribute("description", label.getDescription());
167 }
168 return le;
169 }
170
171 static Element getElement(MCRCategory category, Map<MCRCategoryID, Number> countMap) {
172 Element ce = new Element("category");
173 ce.setAttribute("ID", category.getId().getID());
174 Number number = (countMap == null) ? null : countMap.get(category.getId());
175 if (number != null) {
176 ce.setAttribute("counter", Integer.toString(number.intValue()));
177 }
178 for (MCRLabel label : category.getLabels()) {
179 ce.addContent(getElement(label));
180 }
181 if (category.getURI() != null) {
182 URI link = category.getURI();
183 ce.addContent(getElement(link));
184 }
185 for (MCRCategory cat : category.getChildren()) {
186 ce.addContent(getElement(cat, countMap));
187 }
188 return ce;
189 }
190
191 static Element getElement(URI link) {
192 Element le = new Element("url");
193 le.setAttribute("href", link.toString(), XLINK_NAMESPACE);
194 // TODO: Have to check url here: any samples?
195 le.setAttribute("type", "locator", XLINK_NAMESPACE);
196 return le;
197 }
198
199 static boolean stringNotEmpty(String test) {
200 if (test != null && test.length() > 0) {
201 return true;
202 }
203 return false;
204 }
205 }
206
207 private static class ItemElementFactory {
208 private static final Pattern TEXT_PATTERN = Pattern.compile("\\{text\\}");
209
210 private static final Pattern ID_PATTERN = Pattern.compile("\\{id\\}");
211
212 private static final Pattern DESCR_PATTERN = Pattern.compile("\\{description\\}");
213
214 private static final Pattern COUNT_PATTERN = Pattern.compile("\\{count(:([^\\)]+))?\\}");
215
216 private String labelFormat;
217
218 private boolean emptyLeaves;
219
220 private Map<MCRCategoryID, Number> countMap = null;
221
222 private Map<MCRCategoryID, Boolean> linkedMap = null;
223
224 private Element root;
225
226 ItemElementFactory(MCRCategory cl, String labelFormat, boolean sort, boolean emptyLeaves) {
227 this.labelFormat = labelFormat;
228 this.emptyLeaves = emptyLeaves;
229
230 Matcher countMatcher = COUNT_PATTERN.matcher(labelFormat);
231 /*
232 * countMatcher.group(0) is the whole expression string like
233 * {count:document} countMatcher.group(1) is first inner expression
234 * string like :document countMatcher.group(2) is most inner
235 * expression string like document
236 */
237 if (countMatcher.find()) {
238 if (countMatcher.group(1) == null)
239 countMap = MCRCategLinkServiceFactory.getInstance().countLinks(cl, false);
240 else {
241 // group(2) contains objectType
242 String objectType = countMatcher.group(2);
243 countMap = MCRCategLinkServiceFactory.getInstance().countLinksForType(cl, objectType, false);
244 }
245 }
246 if (!emptyLeaves) {
247 linkedMap = MCRCategLinkServiceFactory.getInstance().hasLinks(cl);
248 }
249
250 root = new Element("items");
251 for (MCRCategory category : cl.getChildren()) {
252 addChildren(root, category);
253 }
254 if (sort) {
255 @SuppressWarnings("unchecked")
256 final List<Element> items = root.getChildren("item");
257 sortItems(items);
258 }
259 }
260
261 Element getResult() {
262 return root;
263 }
264
265 void addChildren(Element parent, MCRCategory category) {
266 if ((!emptyLeaves) && (!linkedMap.get(category.getId()).booleanValue()))
267 return;
268
269 Element ce = new Element("item");
270 ce.setAttribute("value", category.getId().getID());
271 parent.addContent(ce);
272
273 for (MCRLabel label : category.getLabels()) {
274 addLabel(ce, label, category);
275 }
276 for (MCRCategory cat : category.getChildren()) {
277 addChildren(ce, cat);
278 }
279 }
280
281 void addLabel(Element item, MCRLabel label, MCRCategory cat) {
282 Element le = new Element("label");
283 item.addContent(le);
284 if ((label.getLang() != null) && (label.getLang().length() > 0)) {
285 le.setAttribute("lang", label.getLang(), XML_NAMESPACE);
286 }
287
288 String labtext = (label.getText() != null ? label.getText() : "");
289 String labdesc = (label.getDescription() != null ? label.getDescription() : "");
290
291 String text = TEXT_PATTERN.matcher(labelFormat).replaceAll(labtext);
292 text = ID_PATTERN.matcher(text).replaceAll(cat.getId().getID());
293 text = DESCR_PATTERN.matcher(text).replaceAll(labdesc);
294 int num = (countMap == null ? -1 : countMap.get(cat.getId()).intValue());
295 if (num >= 0) {
296 text = COUNT_PATTERN.matcher(text).replaceAll(String.valueOf(num));
297 }
298
299 le.setText(text);
300 }
301
302 @SuppressWarnings("unchecked")
303 private void sortItems(List<Element> items) {
304 sort(items, MCREditorItemComparator.CURRENT_LANG_TEXT_ORDER);
305 Iterator<Element> it = items.iterator();
306 while (it.hasNext()) {
307 Element item = it.next();
308 List<Element> children = item.getChildren("item");
309 if (children.size() > 0) {
310 sortItems(children);
311 }
312 }
313 }
314
315 private void sort(List<Element> list, Comparator<Element> c) {
316 Element[] a = list.toArray(new Element[list.size()]);
317 Arrays.sort(a, c);
318 for (int i = 0; i < a.length; i++) {
319 a[i].detach();
320 }
321 for (int i = 0; i < a.length; i++) {
322 list.add(a[i]);
323 }
324 }
325 }
326 }