001/* 002 * Copyright 2010 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.repository.metadata; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.util.ArrayList; 021import java.util.Date; 022import java.util.LinkedHashMap; 023import java.util.List; 024import java.util.Locale; 025import java.util.Map; 026 027import org.apache.avalon.framework.component.Component; 028import org.apache.avalon.framework.context.Context; 029import org.apache.avalon.framework.context.ContextException; 030import org.apache.avalon.framework.context.Contextualizable; 031import org.apache.avalon.framework.logger.AbstractLogEnabled; 032import org.apache.avalon.framework.service.ServiceException; 033import org.apache.avalon.framework.service.ServiceManager; 034import org.apache.avalon.framework.service.Serviceable; 035import org.apache.cocoon.xml.AttributesImpl; 036import org.apache.cocoon.xml.XMLUtils; 037import org.apache.commons.io.IOUtils; 038import org.apache.commons.lang.StringUtils; 039import org.xml.sax.ContentHandler; 040import org.xml.sax.SAXException; 041 042import org.ametys.core.user.User; 043import org.ametys.core.user.UserIdentity; 044import org.ametys.core.user.UserManager; 045import org.ametys.core.util.DateUtils; 046import org.ametys.core.util.JSONUtils; 047import org.ametys.plugins.repository.AmetysObject; 048import org.ametys.plugins.repository.AmetysObjectIterable; 049import org.ametys.plugins.repository.AmetysObjectResolver; 050import org.ametys.plugins.repository.AmetysRepositoryException; 051import org.ametys.plugins.repository.TraversableAmetysObject; 052import org.ametys.plugins.repository.data.holder.DataHolder; 053import org.ametys.plugins.repository.metadata.CompositeMetadata.MetadataType; 054import org.ametys.runtime.i18n.I18nizableText; 055import org.ametys.runtime.parameter.Enumerator; 056 057/** 058 * Component for helping SAXing metadata. 059 * @deprecated Use {@link DataHolder#dataToSAX} methods instead of this helper 060 */ 061@Deprecated 062public class MetadataSaxer extends AbstractLogEnabled implements Component, Serviceable, Contextualizable 063{ 064 /** The Avalon Role. */ 065 public static final String ROLE = MetadataSaxer.class.getName(); 066 067 /** The JSON conversion utilities. */ 068 protected JSONUtils _jsonUtils; 069 /** Ametys object resolver */ 070 protected AmetysObjectResolver _resolver; 071 /** The avalon context. */ 072 protected Context _context; 073 /** The user manager */ 074 protected UserManager _userManager; 075 076 @Override 077 public void contextualize(Context context) throws ContextException 078 { 079 _context = context; 080 } 081 082 @Override 083 public void service(ServiceManager manager) throws ServiceException 084 { 085 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 086 _jsonUtils = (JSONUtils) manager.lookup(JSONUtils.ROLE); 087 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 088 } 089 090 /** 091 * SAXes a composite metadata. 092 * @param contentHandler the content handler where to SAX into. 093 * @param compositeMetadata the composite metadata. 094 * @throws AmetysRepositoryException if an error occurs. 095 * @throws SAXException if an error occurs. 096 * @throws IOException if an error occurs. 097 */ 098 public void saxMetadata(ContentHandler contentHandler, CompositeMetadata compositeMetadata) throws AmetysRepositoryException, SAXException, IOException 099 { 100 _saxAllMetadata(contentHandler, compositeMetadata, "", null); 101 } 102 103 /** 104 * SAXes a composite metadata. 105 * @param contentHandler the content handler where to SAX into. 106 * @param compositeMetadata the composite metadata. 107 * @param locale The locale to use for localized metadata, such as {@link MultilingualString}. Can be null to sax all existing locales. 108 * @throws AmetysRepositoryException if an error occurs. 109 * @throws SAXException if an error occurs. 110 * @throws IOException if an error occurs. 111 */ 112 public void saxMetadata(ContentHandler contentHandler, CompositeMetadata compositeMetadata, Locale locale) throws AmetysRepositoryException, SAXException, IOException 113 { 114 _saxAllMetadata(contentHandler, compositeMetadata, "", locale); 115 } 116 117 /** 118 * SAX all the metadata of a given composite metadata, with the given prefix. 119 * @param contentHandler the content handler where to SAX into. 120 * @param metadata the composite metadata. 121 * @param prefix the metadata path prefix. 122 * @param locale The locale to use for localized metadata, such as {@link MultilingualString}. Can be null to sax all existing locales. 123 * @throws AmetysRepositoryException if an error occurs. 124 * @throws SAXException if an error occurs. 125 * @throws IOException if an error occurs. 126 */ 127 protected void _saxAllMetadata(ContentHandler contentHandler, CompositeMetadata metadata, String prefix, Locale locale) throws AmetysRepositoryException, SAXException, IOException 128 { 129 for (String metadataName : metadata.getMetadataNames()) 130 { 131 if (metadata.hasMetadata(metadataName)) 132 { 133 _saxMetadata(contentHandler, metadata, metadataName, prefix, locale); 134 } 135 } 136 } 137 138 /** 139 * SAX a single metadata. 140 * @param contentHandler the content handler where to SAX into. 141 * @param parentMetadata the parent composite metadata. 142 * @param metadataName the metadata name. 143 * @param prefix the metadata path prefix. 144 * @param locale The locale to use for localized metadata, such as {@link MultilingualString}. Can be null to sax all existing locales. 145 * @throws AmetysRepositoryException if an error occurs. 146 * @throws SAXException if an error occurs. 147 * @throws IOException if an error occurs. 148 */ 149 protected void _saxMetadata(ContentHandler contentHandler, CompositeMetadata parentMetadata, String metadataName, String prefix, Locale locale) throws AmetysRepositoryException, SAXException, IOException 150 { 151 if (parentMetadata.hasMetadata(metadataName)) 152 { 153 MetadataType metadataType = parentMetadata.getType(metadataName); 154 155 switch (metadataType) 156 { 157 case COMPOSITE: 158 CompositeMetadata subMetadata = parentMetadata.getCompositeMetadata(metadataName); 159 160 XMLUtils.startElement(contentHandler, metadataName); 161 162 // SAX all metadata contains in the current CompositeMetadata. 163 _saxAllMetadata(contentHandler, subMetadata, prefix + metadataName + "/", locale); 164 165 XMLUtils.endElement(contentHandler, metadataName); 166 break; 167 case USER: 168 _saxUserMetadata(contentHandler, parentMetadata, metadataName); 169 break; 170 case BINARY: 171 _saxBinaryMetadata(contentHandler, parentMetadata, metadataName, prefix); 172 break; 173 174 case RICHTEXT: 175 _saxRichTextMetadata(contentHandler, parentMetadata, metadataName, prefix); 176 break; 177 178 case DATE: 179 _saxDateMetadata(contentHandler, parentMetadata, metadataName, prefix); 180 break; 181 182 case OBJECT_COLLECTION: 183 TraversableAmetysObject objectCollection = parentMetadata.getObjectCollection(metadataName); 184 185 XMLUtils.startElement(contentHandler, metadataName); 186 187 // SAX all sub-objects. 188 for (AmetysObject subObject : objectCollection.getChildren()) 189 { 190 _saxObject(contentHandler, subObject, prefix + metadataName + "/", false, locale); 191 } 192 193 XMLUtils.endElement(contentHandler, metadataName); 194 break; 195 196 case MULTILINGUAL_STRING: 197 _saxMultilingualStringMetadata(contentHandler, parentMetadata, metadataName, prefix, locale); 198 break; 199 default: 200 _saxStringMetadata(contentHandler, parentMetadata, metadataName, prefix); 201 break; 202 } 203 } 204 } 205 206 /** 207 * SAX a binary metadata. 208 * @param contentHandler the content handler where to SAX into. 209 * @param parentMetadata the parent composite metadata. 210 * @param metadataName the metadata name. 211 * @param prefix the metadata path prefix. 212 * @throws AmetysRepositoryException if an error occurs. 213 * @throws SAXException if an error occurs. 214 */ 215 protected void _saxBinaryMetadata(ContentHandler contentHandler, CompositeMetadata parentMetadata, String metadataName, String prefix) throws AmetysRepositoryException, SAXException 216 { 217 BinaryMetadata value = parentMetadata.getBinaryMetadata(metadataName); 218 String filename = value.getFilename(); 219 AttributesImpl attrs = new AttributesImpl(); 220 attrs.addCDATAAttribute("type", "attribute"); 221 attrs.addCDATAAttribute("mime-type", value.getMimeType()); 222 attrs.addCDATAAttribute("path", prefix + metadataName); 223 224 if (filename != null) 225 { 226 attrs.addCDATAAttribute("filename", filename); 227 } 228 attrs.addCDATAAttribute("size", String.valueOf(value.getLength())); 229 attrs.addCDATAAttribute("lastModified", DateUtils.dateToString(value.getLastModified())); 230 231 XMLUtils.createElement(contentHandler, metadataName, attrs); 232 } 233 234 /** 235 * SAX a rich-text metadata. 236 * @param contentHandler the content handler where to SAX into. 237 * @param parentMetadata the parent composite metadata. 238 * @param metadataName the metadata name. 239 * @param prefix the metadata path prefix. 240 * @throws AmetysRepositoryException if an error occurs. 241 * @throws SAXException if an error occurs. 242 * @throws IOException if an error occurs. 243 */ 244 protected void _saxRichTextMetadata(ContentHandler contentHandler, CompositeMetadata parentMetadata, String metadataName, String prefix) throws AmetysRepositoryException, SAXException, IOException 245 { 246 RichText richText = parentMetadata.getRichText(metadataName); 247 AttributesImpl attrs = new AttributesImpl(); 248 249 attrs.addCDATAAttribute("mime-type", richText.getMimeType()); 250 attrs.addCDATAAttribute("lastModified", DateUtils.dateToString(richText.getLastModified())); 251 252 XMLUtils.startElement(contentHandler, metadataName, attrs); 253 254 String encoding = richText.getEncoding(); 255 256 try (InputStream is = richText.getInputStream()) 257 { 258 XMLUtils.data(contentHandler, IOUtils.toString(is, encoding)); 259 } 260 261 XMLUtils.endElement(contentHandler, metadataName); 262 } 263 264 /** 265 * SAX a string metadata. 266 * @param contentHandler the content handler where to SAX into. 267 * @param parentMetadata the parent composite metadata. 268 * @param metadataName the metadata name. 269 * @param prefix the metadata path prefix. 270 * @throws AmetysRepositoryException if an error occurs. 271 * @throws SAXException if an error occurs. 272 */ 273 protected void _saxStringMetadata(ContentHandler contentHandler, CompositeMetadata parentMetadata, String metadataName, String prefix) throws AmetysRepositoryException, SAXException 274 { 275 String[] values = parentMetadata.getStringArray(metadataName, new String[0]); 276 277 for (String value : values) 278 { 279 XMLUtils.createElement(contentHandler, metadataName, value); 280 } 281 } 282 283 /** 284 * SAX a multilingual string metadata. 285 * @param contentHandler the content handler where to SAX into. 286 * @param parentMetadata the parent composite metadata. 287 * @param metadataName the metadata name. 288 * @param prefix the metadata path prefix 289 * @param currentLocale The current locale. Can be null. 290 * @throws AmetysRepositoryException if an error occurs. 291 * @throws SAXException if an error occurs. 292 */ 293 protected void _saxMultilingualStringMetadata(ContentHandler contentHandler, CompositeMetadata parentMetadata, String metadataName, String prefix, Locale currentLocale) throws AmetysRepositoryException, SAXException 294 { 295 if (parentMetadata.hasMetadata(metadataName)) 296 { 297 MultilingualString multilingualString = parentMetadata.getMultilingualString(metadataName); 298 MultilingualStringHelper.sax(contentHandler, metadataName, multilingualString, currentLocale); 299 } 300 } 301 302 /** 303 * SAX a string metadata. 304 * @param contentHandler the content handler where to SAX into. 305 * @param parentMetadata the parent composite metadata. 306 * @param metadataName the metadata name. 307 * @param enumerator The enumerator for labelisable values 308 * @throws AmetysRepositoryException if an error occurs. 309 * @throws SAXException if an error occurs. 310 */ 311 protected void _saxEnumeratedStringMetadata(ContentHandler contentHandler, CompositeMetadata parentMetadata, String metadataName, Enumerator enumerator) throws AmetysRepositoryException, SAXException 312 { 313 String[] values = parentMetadata.getStringArray(metadataName, new String[0]); 314 315 for (String value : values) 316 { 317 if (StringUtils.isEmpty(value)) 318 { 319 XMLUtils.createElement(contentHandler, metadataName); 320 } 321 else 322 { 323 AttributesImpl attrs = new AttributesImpl(); 324 attrs.addCDATAAttribute("value", value); 325 326 try 327 { 328 I18nizableText i18n = enumerator.getEntry(value); 329 if (i18n != null) 330 { 331 XMLUtils.startElement(contentHandler, metadataName, attrs); 332 i18n.toSAX(contentHandler); 333 XMLUtils.endElement(contentHandler, metadataName); 334 } 335 else 336 { 337 XMLUtils.createElement(contentHandler, metadataName, attrs, value); 338 } 339 } 340 catch (Exception e) 341 { 342 getLogger().warn("Saxing enumerated String metadata '" + metadataName + "' required a label for enumerated value", e); 343 XMLUtils.createElement(contentHandler, metadataName, attrs, value); 344 } 345 } 346 347 } 348 } 349 350 /** 351 * SAX a metadata as a Date. 352 * @param contentHandler the content handler where to SAX into. 353 * @param parentMetadata the parent composite metadata. 354 * @param metadataName the metadata name. 355 * @param prefix the metadata path prefix. 356 * @throws AmetysRepositoryException if an error occurs. 357 * @throws SAXException if an error occurs. 358 */ 359 protected void _saxDateMetadata(ContentHandler contentHandler, CompositeMetadata parentMetadata, String metadataName, String prefix) throws AmetysRepositoryException, SAXException 360 { 361 Date[] values = parentMetadata.getDateArray(metadataName, new Date[0]); 362 363 for (Date value : values) 364 { 365 XMLUtils.createElement(contentHandler, metadataName, DateUtils.dateToString(value)); 366 } 367 } 368 369 /** 370 * SAX a metadata as a User. 371 * @param contentHandler the content handler where to SAX into. 372 * @param parentMetadata the parent composite metadata. 373 * @param metadataName the metadata name. 374 * @throws SAXException if an error occurs 375 */ 376 protected void _saxUserMetadata(ContentHandler contentHandler, CompositeMetadata parentMetadata, String metadataName) throws SAXException 377 { 378 UserIdentity[] values = parentMetadata.getUserArray(metadataName); 379 380 for (UserIdentity userIdentity : values) 381 { 382 User user = _userManager.getUser(userIdentity); 383 if (user != null) 384 { 385 AttributesImpl attrs = new AttributesImpl(); 386 attrs.addCDATAAttribute("login", userIdentity.getLogin()); 387 attrs.addCDATAAttribute("populationId", userIdentity.getPopulationId()); 388 attrs.addCDATAAttribute("email", user.getEmail()); 389 XMLUtils.createElement(contentHandler, metadataName, attrs, user.getFullName()); 390 } 391 } 392 } 393 394 /** 395 * SAX a metadata as a User as JSON. 396 * @param contentHandler the content handler where to SAX into. 397 * @param parentMetadata the parent composite metadata. 398 * @param metadataName the metadata name. 399 * @throws SAXException if an error occurs 400 */ 401 protected void _saxSingleUserMetadataAsJson(ContentHandler contentHandler, CompositeMetadata parentMetadata, String metadataName) throws SAXException 402 { 403 UserIdentity userIdentity = parentMetadata.getUser(metadataName); 404 405 Map<String, Object> values = _userAsJson(userIdentity); 406 AttributesImpl attrs = new AttributesImpl(); 407 attrs.addCDATAAttribute("json", "true"); 408 409 String jsonString = _jsonUtils.convertObjectToJson(values); 410 XMLUtils.createElement(contentHandler, metadataName, attrs, jsonString); 411 } 412 413 /** 414 * SAX a metadata as a User as JSON. 415 * @param contentHandler the content handler where to SAX into. 416 * @param parentMetadata the parent composite metadata. 417 * @param metadataName the metadata name. 418 * @throws SAXException if an error occurs 419 */ 420 protected void _saxMultipleUserMetadataAsJson(ContentHandler contentHandler, CompositeMetadata parentMetadata, String metadataName) throws SAXException 421 { 422 UserIdentity[] userIdentities = parentMetadata.getUserArray(metadataName); 423 424 List<Map<String, Object>> values = new ArrayList<>(); 425 for (UserIdentity userIdentity : userIdentities) 426 { 427 values.add(_userAsJson(userIdentity)); 428 } 429 430 AttributesImpl attrs = new AttributesImpl(); 431 attrs.addCDATAAttribute("json", "true"); 432 433 String jsonString = _jsonUtils.convertObjectToJson(values); 434 XMLUtils.createElement(contentHandler, metadataName, attrs, jsonString); 435 } 436 437 /** 438 * SAX an Object, i.e. its ID and name, and optionally its metadata. 439 * @param contentHandler the content handler where to SAX into. 440 * @param object the {@link AmetysObject} to sax. 441 * @param prefix the metadata path prefix. 442 * @param isDeep If <code>true</code> and the AmetysObject is a {@link MetadataAwareAmetysObject}, its metadata will be saxed. 443 * @param locale The locale to use for localized metadata, such as {@link MultilingualString}. Can be null to sax all existing locales. 444 * @throws AmetysRepositoryException if an error occurs. 445 * @throws SAXException if an error occurs. 446 * @throws IOException if an error occurs. 447 */ 448 protected void _saxObject(ContentHandler contentHandler, AmetysObject object, String prefix, boolean isDeep, Locale locale) throws AmetysRepositoryException, IOException, SAXException 449 { 450 AttributesImpl attrs = new AttributesImpl(); 451 attrs.addCDATAAttribute("id", object.getId()); 452 attrs.addCDATAAttribute("name", object.getName()); 453 454 XMLUtils.startElement(contentHandler, "object", attrs); 455 456 if (object instanceof MetadataAwareAmetysObject && isDeep) 457 { 458 CompositeMetadata metaHolder = ((MetadataAwareAmetysObject) object).getMetadataHolder(); 459 460 _saxAllMetadata(contentHandler, metaHolder, prefix, locale); 461 } 462 463 XMLUtils.endElement(contentHandler, "object"); 464 } 465 466 /** 467 * Get the id of child Ametys objects 468 * @param objectCollection The parent object collection 469 * @return The id of child Ametys objects 470 */ 471 protected List<String> _getRefAmetysObjectIds (TraversableAmetysObject objectCollection) 472 { 473 List<String> refAOs = new ArrayList<>(); 474 475 try (AmetysObjectIterable<AmetysObject> children = objectCollection.getChildren()) 476 { 477 for (AmetysObject refAO : children) 478 { 479 refAOs.add(refAO.getId()); 480 } 481 } 482 483 return refAOs; 484 } 485 486 /** 487 * Get the JSON representation of a User metadata 488 * @param userIdentity The user identity 489 * @return The user as JSON 490 */ 491 protected Map<String, Object> _userAsJson (UserIdentity userIdentity) 492 { 493 Map<String, Object> json = new LinkedHashMap<>(); 494 495 json.put("login", userIdentity.getLogin()); 496 json.put("populationId", userIdentity.getPopulationId()); 497 498 return json; 499 } 500 501 /** 502 * Get the JSON representation of a {@link BinaryMetadata} 503 * @param binaryMetadata The metadata 504 * @param prefix The prefix 505 * @param metadataName The metadata name 506 * @return The binary as JSON 507 */ 508 protected Map<String, Object> _binaryAsJson (BinaryMetadata binaryMetadata, String prefix, String metadataName) 509 { 510 Map<String, Object> json = new LinkedHashMap<>(); 511 512 String filename = binaryMetadata.getFilename(); 513 514 json.put("type", "attribute"); 515 json.put("mimeType", binaryMetadata.getMimeType()); 516 json.put("path", prefix + metadataName); 517 518 if (filename != null) 519 { 520 json.put("filename", filename); 521 } 522 json.put("size", String.valueOf(binaryMetadata.getLength())); 523 json.put("lastModified", DateUtils.dateToString(binaryMetadata.getLastModified())); 524 525 return json; 526 } 527}