001/* 002 * Copyright 2014 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.contentio.in; 017 018import java.io.IOException; 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.HashMap; 023import java.util.HashSet; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028 029import org.apache.avalon.framework.configuration.Configurable; 030import org.apache.avalon.framework.configuration.Configuration; 031import org.apache.avalon.framework.configuration.ConfigurationException; 032import org.apache.avalon.framework.service.ServiceException; 033import org.apache.avalon.framework.service.ServiceManager; 034import org.apache.avalon.framework.service.Serviceable; 035import org.apache.commons.io.FilenameUtils; 036import org.apache.commons.lang3.StringUtils; 037 038import org.ametys.cms.repository.Content; 039import org.ametys.cms.repository.ContentQueryHelper; 040import org.ametys.cms.repository.WorkflowAwareContent; 041import org.ametys.cms.workflow.AbstractContentWorkflowComponent; 042import org.ametys.cms.workflow.ContentWorkflowHelper; 043import org.ametys.cms.workflow.EditContentFunction; 044import org.ametys.plugins.repository.AmetysObjectIterable; 045import org.ametys.plugins.repository.AmetysObjectResolver; 046import org.ametys.plugins.repository.jcr.NameHelper; 047import org.ametys.plugins.repository.query.expression.AndExpression; 048import org.ametys.plugins.repository.query.expression.Expression; 049import org.ametys.plugins.repository.query.expression.Expression.Operator; 050import org.ametys.plugins.repository.query.expression.StringExpression; 051import org.ametys.plugins.workflow.AbstractWorkflowComponent; 052import org.ametys.runtime.plugin.component.AbstractLogEnabled; 053 054import com.opensymphony.workflow.WorkflowException; 055 056/** 057 * Abstract {@link ContentImporter} class which provides base importer configuration and logic.<br> 058 * Configuration options: 059 * <ul> 060 * <li>Importer priority</li> 061 * <li>Allowed extensions, without leading dot and comma-separated</li> 062 * <li>Content types and mixins of the created contents</li> 063 * <li>Language of the created contents</li> 064 * <li>Content workflow name and creation action ID</li> 065 * </ul><br> 066 * Example configuration handled by the configure method: 067 * <pre> 068 * <extension point="org.ametys.plugins.contentio.ContentImporterExtensionPoint" 069 * id="my.content.importer" 070 * class="..."> 071 * <priority>500</priority> 072 * <extensions>ext,ext2</extensions> 073 * <content-creation> 074 * <content-types>My.ContentType.1,My.ContentType.2</content-types> 075 * <mixins>My.Mixin.1,My.Mixin.2</mixins> 076 * <language>en</language> 077 * <workflow name="content" createActionId="1" editActionId="2"/> 078 * </content-creation> 079 * </extension> 080 * </pre> 081 */ 082public abstract class AbstractContentImporter extends AbstractLogEnabled implements ContentImporter, Serviceable, Configurable 083{ 084 085 /** The default importer priority. */ 086 protected static final int DEFAULT_PRIORITY = 5000; 087 088 /** Map used to store the mapping from "local" ID to content ID, when actually imported. */ 089 protected static final String _CONTENT_ID_MAP_KEY = AbstractContentImporter.class.getName() + "$contentIdMap"; 090 091 /** Map used to store the content references, indexed by content and metadata path. */ 092 protected static final String _CONTENT_LINK_MAP_KEY = AbstractContentImporter.class.getName() + "$contentLinkMap"; 093 094 /** Map used to store the content repeater sizes. */ 095 protected static final String _CONTENT_REPEATER_SIZE_MAP = AbstractContentImporter.class.getName() + "$contentRepeaterSizeMap"; 096 097 /** The AmetysObject resolver. */ 098 protected AmetysObjectResolver _resolver; 099 100 /** The content workflow helper. */ 101 protected ContentWorkflowHelper _contentWorkflowHelper; 102 103 /** The importer priority. */ 104 protected int _priority = DEFAULT_PRIORITY; 105 106 /** The allowed extensions. */ 107 protected Set<String> _extensions; 108 109 /** The imported contents' types. */ 110 protected String[] _contentTypes; 111 112 /** The imported contents' mixins. */ 113 protected String[] _mixins; 114 115 /** The importer contents' language. */ 116 protected String _language; 117 118 /** The importer contents' workflow name. */ 119 protected String _workflowName; 120 121 /** The importer contents' initial action ID. */ 122 protected int _initialActionId; 123 124 /** The importer contents' edition action ID. */ 125 protected int _editActionId; 126 127 @Override 128 public void service(ServiceManager manager) throws ServiceException 129 { 130 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 131 _contentWorkflowHelper = (ContentWorkflowHelper) manager.lookup(ContentWorkflowHelper.ROLE); 132 } 133 134 @Override 135 public void configure(Configuration configuration) throws ConfigurationException 136 { 137 _priority = configuration.getChild("priority").getValueAsInteger(DEFAULT_PRIORITY); 138 139 configureExtensions(configuration.getChild("extensions")); 140 141 configureContentCreation(configuration.getChild("content-creation")); 142 } 143 144 /** 145 * Configure the allowed extensions. 146 * @param configuration the extension configuration. 147 * @throws ConfigurationException if an error occurs. 148 */ 149 protected void configureExtensions(Configuration configuration) throws ConfigurationException 150 { 151 _extensions = new HashSet<>(); 152 153 String extensionsStr = configuration.getValue(""); 154 155 if (StringUtils.isBlank(extensionsStr)) 156 { 157 _extensions.addAll(getDefaultExtensions()); 158 } 159 else 160 { 161 for (String ext : StringUtils.split(extensionsStr, ", ")) 162 { 163 String extension = ext.trim(); 164 if (extension.startsWith(".")) 165 { 166 extension = extension.substring(1); 167 } 168 169 _extensions.add(extension); 170 } 171 } 172 } 173 174 /** 175 * Configure the content creation parameters. 176 * @param configuration the content creation configuration. 177 * @throws ConfigurationException if an error occurs. 178 */ 179 protected void configureContentCreation(Configuration configuration) throws ConfigurationException 180 { 181 String typesStr = configuration.getChild("content-types").getValue(); 182 _contentTypes = StringUtils.split(typesStr, ", "); 183 184 String mixins = configuration.getChild("mixins").getValue(""); // mixins can be empty 185 _mixins = StringUtils.split(mixins, ", "); 186 187 _language = configuration.getChild("language").getValue(); 188 189 configureWorkflow(configuration); 190 } 191 192 /** 193 * Configure the content workflow. 194 * @param configuration the content creation configuration. 195 * @throws ConfigurationException if an error occurs. 196 */ 197 protected void configureWorkflow(Configuration configuration) throws ConfigurationException 198 { 199 Configuration wfConf = configuration.getChild("workflow"); 200 201 _workflowName = wfConf.getAttribute("name"); 202 203 _initialActionId = wfConf.getAttributeAsInteger("createActionId"); 204 _editActionId = wfConf.getAttributeAsInteger("editActionId"); 205 } 206 207 @Override 208 public int getPriority() 209 { 210 return _priority; 211 } 212 213 /** 214 * Get the default allowed extensions. 215 * @return the default allowed extensions, without leading dots. Cannot be null. 216 */ 217 protected Collection<String> getDefaultExtensions() 218 { 219 return Collections.emptySet(); 220 } 221 222 /** 223 * Test if the given filename has a supported extension. 224 * @param name the name, can't be null. 225 * @return true if the extension is supported, false otherwise. 226 * @throws IOException if an error occurs. 227 */ 228 protected boolean isExtensionValid(String name) throws IOException 229 { 230 return _extensions.isEmpty() || _extensions.contains(FilenameUtils.getExtension(name)); 231 } 232 233 /** 234 * The content types of a created content. 235 * @param params the import parameters. 236 * @return the content types of a created content. 237 */ 238 protected String[] getContentTypes(Map<String, Object> params) 239 { 240 return _contentTypes; 241 } 242 243 /** 244 * The mixins of a created content. 245 * @param params the import parameters. 246 * @return The mixins of a created content. 247 */ 248 protected String[] getMixins(Map<String, Object> params) 249 { 250 return _mixins; 251 } 252 253 /** 254 * The language of a created content. 255 * @param params the import parameters. 256 * @return The language of a created content. 257 */ 258 protected String getLanguage(Map<String, Object> params) 259 { 260 return _language; 261 } 262 263 /** 264 * The workflow name of a created content. 265 * @param params the import parameters. 266 * @return The workflow name of a created content. 267 */ 268 protected String getWorkflowName(Map<String, Object> params) 269 { 270 return _workflowName; 271 } 272 273 /** 274 * The workflow creation action ID of a created content. 275 * @param params the import parameters. 276 * @return The workflow creation action ID of a created content. 277 */ 278 protected int getInitialActionId(Map<String, Object> params) 279 { 280 return _initialActionId; 281 } 282 283 /** 284 * The workflow action ID used to edit a content. 285 * @param params the import parameters. 286 * @return The workflow action ID used to edit a content. 287 */ 288 protected int getEditActionId(Map<String, Object> params) 289 { 290 return _editActionId; 291 } 292 293 /** 294 * Get the map used to store the mapping from "local" ID (defined in the import file) 295 * to the AmetysObject ID of the contents, when actually imported. 296 * @param params the import parameters. 297 * @return the content "local to repository" ID map. 298 */ 299 protected Map<String, String> getContentIdMap(Map<String, Object> params) 300 { 301 // Get or create the map in the global parameters. 302 @SuppressWarnings("unchecked") 303 Map<String, String> contentIdMap = (Map<String, String>) params.get(_CONTENT_ID_MAP_KEY); 304 if (contentIdMap == null) 305 { 306 contentIdMap = new HashMap<>(); 307 params.put(_CONTENT_ID_MAP_KEY, contentIdMap); 308 } 309 310 return contentIdMap; 311 } 312 313 /** 314 * Get the map used to store the content references. 315 * The Map is shaped like: referencing content -> local metadata path -> content references. 316 * @param params the import parameters. 317 * @return the content reference map. 318 */ 319 protected Map<Content, Map<String, Object>> getContentRefMap(Map<String, Object> params) 320 { 321 // Get or create the map in the global parameters. 322 @SuppressWarnings("unchecked") 323 Map<Content, Map<String, Object>> contentRefMap = (Map<Content, Map<String, Object>>) params.get(_CONTENT_LINK_MAP_KEY); 324 if (contentRefMap == null) 325 { 326 contentRefMap = new HashMap<>(); 327 params.put(_CONTENT_LINK_MAP_KEY, contentRefMap); 328 } 329 330 return contentRefMap; 331 } 332 333 /** 334 * Add a content reference to the map. 335 * @param content The referencing content. 336 * @param metadataPath The path of the metadata which holds the content references. 337 * @param reference The content reference. 338 * @param params The import parameters. 339 */ 340 protected void addContentReference(Content content, String metadataPath, ContentReference reference, Map<String, Object> params) 341 { 342 addContentReference(getContentRefMap(params), content, metadataPath, reference); 343 } 344 345 /** 346 * Add a content reference to the map. 347 * @param contentRefMap The content reference map. 348 * @param content The referencing content. 349 * @param metadataPath The path of the metadata which holds the content references. 350 * @param reference The content reference. 351 */ 352 protected void addContentReference(Map<Content, Map<String, Object>> contentRefMap, Content content, String metadataPath, ContentReference reference) 353 { 354 Map<String, Object> contentReferences; 355 if (contentRefMap.containsKey(content)) 356 { 357 contentReferences = contentRefMap.get(content); 358 } 359 else 360 { 361 contentReferences = new HashMap<>(); 362 contentRefMap.put(content, contentReferences); 363 } 364 365 contentReferences.put(metadataPath, reference); 366 } 367 368 /** 369 * Add content references to the map. 370 * @param contentRefMap The content reference map. 371 * @param content The referencing content. 372 * @param metadataPath The path of the metadata which holds the content references. 373 * @param references the content reference list. 374 */ 375 protected void addContentReferences(Map<Content, Map<String, Object>> contentRefMap, Content content, String metadataPath, List<ContentReference> references) 376 { 377 Map<String, Object> contentReferences; 378 if (contentRefMap.containsKey(content)) 379 { 380 contentReferences = contentRefMap.get(content); 381 } 382 else 383 { 384 contentReferences = new HashMap<>(); 385 contentRefMap.put(content, contentReferences); 386 } 387 388 contentReferences.put(metadataPath, references); 389 } 390 391 /** 392 * Get the map used to store the repeater sizes. 393 * The Map is shaped like: referencing content -> local metadata path -> content references. 394 * @param params the import parameters. 395 * @return the content reference map. 396 */ 397 protected Map<Content, Map<String, Integer>> getContentRepeaterSizeMap(Map<String, Object> params) 398 { 399 // Get or create the map in the global parameters. 400 @SuppressWarnings("unchecked") 401 Map<Content, Map<String, Integer>> contentRepeaterSizeMap = (Map<Content, Map<String, Integer>>) params.get(_CONTENT_REPEATER_SIZE_MAP); 402 if (contentRepeaterSizeMap == null) 403 { 404 contentRepeaterSizeMap = new HashMap<>(); 405 params.put(_CONTENT_REPEATER_SIZE_MAP, contentRepeaterSizeMap); 406 } 407 408 return contentRepeaterSizeMap; 409 } 410 411 /** 412 * Set a repeater size in the map (needed to execute the edit content function). 413 * @param content The content containing the repeater. 414 * @param metadataPath The repeater metadata path. 415 * @param repeaterSize The repeater size. 416 * @param params The import parameters. 417 */ 418 protected void setRepeaterSize(Content content, String metadataPath, int repeaterSize, Map<String, Object> params) 419 { 420 Map<Content, Map<String, Integer>> contentRepeaterSizeMap = getContentRepeaterSizeMap(params); 421 422 Map<String, Integer> repeaters; 423 if (contentRepeaterSizeMap.containsKey(content)) 424 { 425 repeaters = contentRepeaterSizeMap.get(content); 426 } 427 else 428 { 429 repeaters = new HashMap<>(); 430 contentRepeaterSizeMap.put(content, repeaters); 431 } 432 433 repeaters.put(metadataPath, repeaterSize); 434 } 435 436 /** 437 * Create a content. 438 * @param title the content title. 439 * @param params the import parameters. 440 * @return the created content. 441 * @throws WorkflowException if an error occurs. 442 */ 443 protected Content createContent(String title, Map<String, Object> params) throws WorkflowException 444 { 445 String[] contentTypes = getContentTypes(params); 446 String[] mixins = getMixins(params); 447 String language = getLanguage(params); 448 String workflowName = getWorkflowName(params); 449 int initialActionId = getInitialActionId(params); 450 451 return createContent(title, contentTypes, mixins, language, workflowName, initialActionId, params); 452 } 453 454 /** 455 * Create a content. 456 * @param title the content title. 457 * @param contentTypes the content types. 458 * @param mixins the content mixins. 459 * @param language the content language. 460 * @param params the import parameters. 461 * @return the created content. 462 * @throws WorkflowException if an error occurs. 463 */ 464 protected Content createContent(String title, String[] contentTypes, String[] mixins, String language, Map<String, Object> params) throws WorkflowException 465 { 466 String workflowName = getWorkflowName(params); 467 int initialActionId = getInitialActionId(params); 468 469 return createContent(title, contentTypes, mixins, language, workflowName, initialActionId, params); 470 } 471 472 /** 473 * Create a content. 474 * @param title the content title. 475 * @param contentTypes the content types. 476 * @param mixins the content mixins. 477 * @param language the content language. 478 * @param parentContentId the parent content ID. 479 * @param parentContentMetadataPath the parent content metadata path. 480 * @param params the import parameters. 481 * @return the created content. 482 * @throws WorkflowException if an error occurs. 483 */ 484 protected Content createContent(String title, String[] contentTypes, String[] mixins, String language, String parentContentId, String parentContentMetadataPath, Map<String, Object> params) throws WorkflowException 485 { 486 String workflowName = getWorkflowName(params); 487 int initialActionId = getInitialActionId(params); 488 489 return createContent(title, contentTypes, mixins, language, workflowName, initialActionId, parentContentId, parentContentMetadataPath, params); 490 } 491 492 /** 493 * Create a content. 494 * @param title the content title. 495 * @param contentTypes the content types. 496 * @param mixins the content mixins. 497 * @param language the content language. 498 * @param workflowName the content workflow name. 499 * @param initialActionId the content create action ID. 500 * @param params the import parameters. 501 * @return the created content. 502 * @throws WorkflowException if an error occurs. 503 */ 504 protected Content createContent(String title, String[] contentTypes, String[] mixins, String language, String workflowName, int initialActionId, Map<String, Object> params) throws WorkflowException 505 { 506 return createContent(title, contentTypes, mixins, language, workflowName, initialActionId, null, null, params); 507 } 508 509 /** 510 * Create a content. 511 * @param title the content title. 512 * @param contentTypes the content types. 513 * @param mixins the content mixins. 514 * @param language the content language. 515 * @param workflowName the content workflow name. 516 * @param initialActionId the content create action ID. 517 * @param parentContentId the parent content ID. 518 * @param parentContentMetadataPath the parent content metadata path. 519 * @param params the import parameters. 520 * @return the created content. 521 * @throws WorkflowException if an error occurs. 522 */ 523 protected Content createContent(String title, String[] contentTypes, String[] mixins, String language, String workflowName, int initialActionId, String parentContentId, String parentContentMetadataPath, Map<String, Object> params) throws WorkflowException 524 { 525 String name; 526 try 527 { 528 name = NameHelper.filterName(title); 529 } 530 catch (Exception e) 531 { 532 // Ignore the exception, just provide a valid start. 533 name = "content-" + title; 534 } 535 536 Map<String, Object> result = _contentWorkflowHelper.createContent(workflowName, initialActionId, name, title, contentTypes, mixins, language, parentContentId, parentContentMetadataPath); 537 538 return (Content) result.get(AbstractContentWorkflowComponent.CONTENT_KEY); 539 } 540 541 /** 542 * Restore content references. 543 * @param params The import parameters. 544 */ 545 protected void restoreContentReferences(Map<String, Object> params) 546 { 547 Map<Content, Map<String, Object>> contentRefMap = getContentRefMap(params); 548 Map<Content, Map<String, Integer>> contentRepeaterSizeMap = getContentRepeaterSizeMap(params); 549 int editActionId = getEditActionId(params); 550 551 for (Content content : contentRefMap.keySet()) 552 { 553 if (content instanceof WorkflowAwareContent) 554 { 555 Map<String, Object> contentReferences = contentRefMap.get(content); 556 Map<String, Integer> repeaters = contentRepeaterSizeMap.get(content); 557 558 Map<String, Object> values = new HashMap<>(); 559 560 // Fill the value map with the content references. 561 setReferenceMetadatas(contentReferences, values, repeaters, params); 562 563 try 564 { 565 if (!values.isEmpty()) 566 { 567 Map<String, Object> contextParameters = new HashMap<>(); 568 contextParameters.put(EditContentFunction.QUIT, true); 569 contextParameters.put(EditContentFunction.FORM_RAW_VALUES, values); 570 571 Map<String, Object> inputs = new HashMap<>(); 572 inputs.put(AbstractWorkflowComponent.CONTEXT_PARAMETERS_KEY, contextParameters); 573 inputs.put(AbstractContentWorkflowComponent.CONTENT_KEY, content); 574 575 _contentWorkflowHelper.doAction((WorkflowAwareContent) content, editActionId, inputs); 576 } 577 } 578 catch (WorkflowException e) 579 { 580 // TODO Throw exception? 581 getLogger().warn("An error occurred restoring content references in content {}", content, e); 582 } 583 } 584 } 585 } 586 587 /** 588 * Fill the value map with the content references. 589 * @param contentReferences The list of content references indexed by metadata path. 590 * @param values The value map passed to the EditContentFunction class. 591 * @param repeaters The repeater sizes for this content. 592 * @param params The import parameters. 593 */ 594 protected void setReferenceMetadatas(Map<String, Object> contentReferences, Map<String, Object> values, Map<String, Integer> repeaters, Map<String, Object> params) 595 { 596 for (String metadataPath : contentReferences.keySet()) 597 { 598 Object value = contentReferences.get(metadataPath); 599 String metaKey = EditContentFunction.FORM_ELEMENTS_PREFIX + metadataPath; 600 601 if (value instanceof List<?>) 602 { 603 // Multiple value 604 @SuppressWarnings("unchecked") 605 List<ContentReference> references = (List<ContentReference>) value; 606 List<String> contentIds = new ArrayList<>(references.size()); 607 for (ContentReference reference : references) 608 { 609 String refContentId = getReferencedContentId(reference, params); 610 if (refContentId != null) 611 { 612 contentIds.add(refContentId); 613 } 614 } 615 616 if (!contentIds.isEmpty()) 617 { 618 values.put(metaKey, contentIds); 619 } 620 } 621 else if (value instanceof ContentReference) 622 { 623 // Single value. 624 String refContentId = getReferencedContentId((ContentReference) value, params); 625 if (refContentId != null) 626 { 627 values.put(metaKey, refContentId); 628 } 629 } 630 } 631 632 if (repeaters != null) 633 { 634 for (String repeaterPath : repeaters.keySet()) 635 { 636 Integer size = repeaters.get(repeaterPath); 637 if (size > 0) 638 { 639 String sizeKey = EditContentFunction.INTERNAL_FORM_ELEMENTS_PREFIX + repeaterPath + "/size"; 640 values.put(sizeKey, repeaters.get(repeaterPath).toString()); 641 } 642 } 643 } 644 } 645 646 /** 647 * Get the content ID from a content reference. 648 * @param contentRef The content reference. 649 * @param params The import parameters. 650 * @return the content ID if it was found, or null otherwise. 651 */ 652 protected String getReferencedContentId(ContentReference contentRef, Map<String, Object> params) 653 { 654 int refType = contentRef.getType(); 655 if (refType == ContentReference.TYPE_LOCAL_ID) 656 { 657 String localId = (String) contentRef.getValue(); 658 String contentId = getContentIdMap(params).get(localId); 659 if (StringUtils.isNotEmpty(contentId) && _resolver.hasAmetysObjectForId(contentId)) 660 { 661 return contentId; 662 } 663 } 664 else if (refType == ContentReference.TYPE_CONTENT_ID) 665 { 666 String contentId = (String) contentRef.getValue(); 667 if (StringUtils.isNotEmpty(contentId) && _resolver.hasAmetysObjectForId(contentId)) 668 { 669 return contentId; 670 } 671 } 672 else if (refType == ContentReference.TYPE_CONTENT_VALUES) 673 { 674 @SuppressWarnings("unchecked") 675 Map<String, String> values = (Map<String, String>) contentRef.getValue(); 676 Content content = getContentFromProperties(values); 677 if (content != null) 678 { 679 return content.getId(); 680 } 681 } 682 683 return null; 684 } 685 686 /** 687 * Search a content from a map of its metadata values. 688 * @param propertyValues The metadata values. 689 * @return The Content if found, null otherwise. 690 */ 691 protected Content getContentFromProperties(Map<String, String> propertyValues) 692 { 693 Content content = null; 694 695 List<Expression> expressions = new ArrayList<>(); 696 for (String property : propertyValues.keySet()) 697 { 698 String value = propertyValues.get(property); 699 expressions.add(new StringExpression(property, Operator.EQ, value)); 700 } 701 702 Expression[] exprArray = expressions.toArray(new Expression[expressions.size()]); 703 704 String query = ContentQueryHelper.getContentXPathQuery(new AndExpression(exprArray)); 705 706 AmetysObjectIterable<Content> contents = _resolver.query(query); 707 Iterator<Content> it = contents.iterator(); 708 709 if (it.hasNext()) 710 { 711 content = it.next(); 712 713 if (it.hasNext()) 714 { 715 content = null; 716 } 717 } 718 719 return content; 720 } 721 722 /** 723 * Class representing a reference to a content in an import file. 724 */ 725 public class ContentReference 726 { 727 /** 728 * The referenced content doesn't exist in the repository, it's in the import file. 729 * The reference value is the content ID in the import file. 730 */ 731 public static final int TYPE_LOCAL_ID = 1; 732 733 /** 734 * The referenced content exits in the repository and its ID is known. 735 * The reference value is the content ID in the repository (AmetysObject ID). 736 */ 737 public static final int TYPE_CONTENT_ID = 2; 738 739 /** 740 * The referenced content exits in the repository. Its ID is not known, 741 * but it can be identified by one or several of its metadata. 742 * The reference value is a Map of metadata name -> value. 743 */ 744 public static final int TYPE_CONTENT_VALUES = 3; 745 746 /** The reference type. */ 747 private int _type; 748 749 /** The reference value, depends on the reference type. */ 750 private Object _value; 751 752 /** 753 * Build a content reference. 754 * @param type the reference type. 755 * @param value the reference value. 756 */ 757 public ContentReference(int type, Object value) 758 { 759 this._type = type; 760 this._value = value; 761 } 762 763 /** 764 * Get the type. 765 * @return the type 766 */ 767 public int getType() 768 { 769 return _type; 770 } 771 772 /** 773 * Set the type. 774 * @param type the type to set 775 */ 776 public void setType(int type) 777 { 778 this._type = type; 779 } 780 781 /** 782 * Get the value. 783 * @return the value 784 */ 785 public Object getValue() 786 { 787 return _value; 788 } 789 790 /** 791 * Set the value. 792 * @param value the value to set 793 */ 794 public void setValue(Object value) 795 { 796 this._value = value; 797 } 798 } 799}