mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	20678: DAO5 branch: Preparation for merge back to HEAD
   20689: Merged DAO4 to DAO5
        - Removed all 'dbscripts/create/3.x/SomeDialect' and replaced with 'dbscripts/create/SomeDialect'
          DB create scripts are taken from latest DAO4
        - TODO: FixAuthoritiesCrcValuesPatch needs query implementation in PatchDAO
        Merged DAO3 to DAO4
           - Reapplied fixes for ALF-713 (race condition on Usages)
           19350: Merged BRANCHES/DEV/V3.3-DAO-REFACTOR-2 to BRANCHES/DEV/V3.3-DAO-REFACTOR-3:
               18939: SAIL-4 :2nd stage branch for DAO refactor off HEAD rev 18898
               18948: Merged V3.3-DAO-REFACTOR to V3.3-DAO-REFACTOR-2
                    18202: Dev branch for DAO refactor
                    18252: SAIL-233: QName.hbm.xml
                    18295: Added missing CREATE TABLE statements for QName-related code
                    18324: SAIL-234: Node.hbm.xml: Node aspects initial integration
                    18355: Added 'setValue' method to manually update the cached value
                    18356: MV property stressing lowered to speed test up
                    18357: SAIL-234: Node.hbm.xml
                    18376: Pulled all Alfresco-related create SQL into script
                    18389: SAIL-234: Permissions DAO refactor - initial checkpoint
                    18390: Formatting only (line-endings)
                    18400: SAIL-234: Node.hbm.xml
                    18418: SAIL-234: Node.hbm.xml: 'alf_node_assoc' CRUD
                    18429: SAIL-234: Node.hbm.xml: Cleaned out all Hibernate references to NodeAssocImpl
                    18457: SAIL-234: Permissions DAO refactor
               18959: Merged DEV/V3.3-DAO-REFACTOR to DEV/V3.3-DAO-REFACTOR-2
                    18479: SAIL-234: Node.hbm.xml - fix updateNode (missing id when saving oldDummyNode)
                    18482: SAIL-235: remove Permissions.hbm.xml
                    18517: SAIL-235: Permissions DAO refactor
                    18523: SAIL-234: Node.hbm.xml
                    18524: SAIL-235: Permissions DAO refactor
               18960: Merged DEV/V3.3-DAO-REFACTOR to DEV/V3.3-DAO-REFACTOR-2
                    18533: Flipped back to Windows line endings
                    18535: Formatting-only (eol)
                    18540: Formatting-only (eol)
                    18541: SAIL-235: Permissions DAO refactor
                    18543: SAIL-234: Node.hbm.xml: Start alf_store changes
                    18567: SAIL-235: Permissions DAO refactor
                    18596: SAIL-305:  Alfresco DDL - formatted/rationalized and added missing indexes & fk constraints
                    18603: SAIL-311: Minor cleanup for schema upgrade scripts (V3.3)
                    18604: SAIL-311: Remove empty dirs
                    18619: SAIL-274: Locale.hbm.xml
                    18621: Added method to create default ACL
                    18622: SAIL-234: Node.hbm.xml: Store, Transaction, Server and some node
                    18624: Formatting only (eol)
                    18631: SAIL-235: Permissions DAO refactor
                    18633: SAIL-235: Permissions DAO refactor - do not expose CRUD for AceContext (or AuthorityAlias) since currently unused
                    18639: getLocale(Locale) should return null if it doesn't exist
                    18640: SAIL-234 NodeDAO: More replacement of node queries and updates
                    18648: SAIL-310: Create SQL script for core repo tables (All DB ports)
                    18651: SAIL-234 NodeDAO: Moves across stores handle presence of target deleted nodes
               18961: Merged DEV/V3.3-DAO-REFACTOR to DEV/V3.3-DAO-REFACTOR-2
                    18658: SAIL-274 Locale DAO: Missing getValueKey() method
                    18662: SAIL-235: Permissions DAO refactor - further cleanup (of DbAccessControlList usage, including copyACLs)
                    18664: DB scripts porting for PostgreSQL finished.
                    18668: SAIL-234 Node DAO: Note in case Transaction Change ID is dropped from indexes
                    18669: SAIL-234 Node DAO: deleteNode and archive (store move) fixes
                    18672: DB scripts porting for Oracle finished. 
                    18675: SAIL-235: Permissions DAO refactor 
                    18677: DB scripts porting for DB2 finished.
               18964: Merged DEV/V3.3-DAO-REFACTOR to DEV/V3.3-DAO-REFACTOR-2
                    18687: Execute a callback with retries
                    18688: SAIL-234 Node DAO: Child association creation
                    18690: SAIL-234 Node DAO: Comment out raw creation of stores as it breaks subsequent bootstrap checks
                    18691: SAIL-234 Node DAO: More replacement of alf_child_assoc handling
                    18713: Commented about needing a more efficient removeChildAssociation method
                    18714: SAIL-234 Node DAO: Replaced queries on alf_child_assoc
                    18715: SAIL-234 Node DAO: More alf_child_assoc query replacement
                    18727: SAIL-234 Node DAO: alf_child_assoc queries complete
                    18737: SAIL-234 Node DAO: Tweaks to newNode and implemented prependPaths
                    18741: SAIL-234 and SAIL-334: Moved UsageDelta Hibernate code and queries over to UsageDeltaDAO
                    18748: SAIL-234 Node DAO: fix NPE (EditionServiceImplTest)
                    18769: SAIL-234 Node DAO: alf_node_properties ground work
                    18786: SAIL-234 Node DAO: alf_node_properties and cm:auditable properties
                    18810: Added EqualsHelper.getMapComparison
                    18813: TransactionalCache propagates cache clears and removals during rollback
                    18826: SAIL-234 Node DAO: Moved over sundry references to NodeDaoService to NodeDAO
                    18849: SAIL-237: UsageDelta.hbm.xml - eol formatting only (including removal of unwanted svn:eol-style=native property)
                    18869: SAIL-234 NodeDAO: Fixed more references to 'nodeDaoService'
                    18895: SAIL-234 NodeDAO: Queries for alf_transaction
                    18899: SAIL-234 Node DAO: Fixed bean fetching for 'nodeDAO'
                    18909: SAIL-234 NodeDAO: Fixes to getNodeRefStatus and various txn queries
                    18916: SAIL-234 NodeDAO: Fixed moveNode alf_child_assoc updates
                    18922: SAIL-235: DAO refactoring: Permission.hbm.xml
                    18930: SAIL-235: DAO refactoring: Permission.hbm.xml
                    18932: SAIL-234 NodeDAO: Fixing up gotchas, javadocs and some naming
                    18933: SAIL-234 NodeDAO: Minor neatening
                    18935: SAIL-234 Node DAO: Caches for ID to NodeRef and StoreRef
                    18936: EHCache config files line endings
                    18938: SAIL-237: Usage DAO refactor - initial checkpoint
                    18945: SAIL-235: DAO refactoring: Permission.hbm.xml. Move Node.
               18975: Fix for move-node ACL jiggery-pokery
               19067: SAIL-4: fix VersionHistoryImpl.getSuccessors (causing VersionServiceImplTest.testGetVersionHistorySameWorkspace failure)
               19068: SAIL-234: fix VersionMigratorTest.testMigrateOneVersion
               19074: SAIL-237: Usage DAO - update to common iBatis mapping pattern(s) to ease DB porting
               19076: SAIL-231: Activities DAO - update to common iBatis mapping pattern(s)
               19077: SAIL-232: AppliedPatch DAO - minor cleanup (comments & formatting only)
               19092: Merging HEAD to DEV/V3.3-DAO-REFACTOR-2
                     18973: Temporarily comment out AVMTestSuite and run AVM tests individually
                     19056: AVM unit test improvements
               19097: SAIL-235: DAO refactoring: Permission.hbm.xml: Additional index to support queries to find the id and acl id for the primary children of a node.
               19185: SAIL-238: Permissions DAO - (minor) update to common iBatis mapping pattern
               19289: SAIL-234 NodeDAO: Node cache replaces NodeRef cache
               19302: SAIL-234 Node DAO: Added cache for node properties
               19318: SAIL-4: AVM DAO - (minor) update to common iBatis mapping pattern
   20690: Merged BRANCHES/DEV/V3.3-DAO-REFACTOR-4 to BRANCHES/DEV/V3.3-DAO-REFACTOR-5:
        20063: (RECORD ONLY) DAO refactor branch V4
        20146: Merged BRANCHES/DEV/V3.3-DAO-REFACTOR-3 to BRANCHES/DEV/V3.3-DAO-REFACTOR-4:
             19401: SAIL-234 Node DAO: Fix permission service tests (setPrimaryChildrenSharedAclId needs to invalidate nodesCache)
             19428: Fixed TransactionalCache issue with null and NullValueMarker
             19429: Took empty cm:content creation out of FileFolderService#createImpl
             19430: SAIL-234 Node DAO: Tweaks around caching and cm:auditable
             19431: SAIL-4 DAO Refactor: Exception thrown when attempting writes in read-only txn have changed
             19436: SAIL-234 Node DAO: Fix NPE during cm:auditable update
             19475: Allow debugging of code without stepping into trivial stuff
             19476: Follow-up on 19429 by ensuring CIFS/FTP set a  mimetype on the ContentWriter
             19477: SAIL-234 Node DAO: Leverage DAO better for NodeService.addProperties
             19478: SAIL-234 NodeDAO: Added toString() for ParentAssocsInfo (cache value for parent assocs)
             19479: SAIL-234 Node DAO: Fixed for parent association and property caches
             19480: Made TransactionAwareSingleton bind-key a GUID
             19481: SAIL-234 Node DAO: Reinstated 100K collection property tests
             19482: SAIL-234 Node DAO: Node and property cache fixes highlighted by unit tests
             19483: SAIL-234 Node DAO: Start on NodeBulkLoader implementation
             19595: SAIL-234 Node DAO: Fix moveNode to detect cyclic relationship prior to updating ACLs for moved tree FileFolderServiceImplTest.testETHREEOH_3088_MoveIntoSelf)
        20147: Merged BRANCHES/DEV/V3.3-DAO-REFACTOR-3 to BRANCHES/DEV/V3.3-DAO-REFACTOR-4:
             19602: (RECORD ONLY) Reintegrated with HEAD up to rev 19433
             19621: (RECORD ONLY) SAIL-347
             19683: (RECORD ONLY) Reverse-merged 19621 for SAIL-347
             19722: (RECORD ONLY) Merged /alfresco/HEAD:r19434-19721
        20150: Merged BRANCHES/DEV/V3.3-DAO-REFACTOR-3 to BRANCHES/DEV/V3.3-DAO-REFACTOR-4:
             19741: Merged DEV\V3.3-DAO-REFACTOR-2 to DEV\V3.3-DAO-REFACTOR-3
                   19739: Extended "move" tests
             19743: Fix AuditableAspectTest.testAddAspect (to allow for node modified date tolerance)
             19748: Remaining part of merge from HEAD to V3.3-DAO-REFACTOR-3
                   19367: Merged BRANCHES/V3.2 to HEAD:
                       19286: Fix for ALF-626 "Using 'null' as an authority argument in clearPermissions() cause a java.lang.NullPointerException"
             19755: SAIL-234 Node DAO: Fix RepoAdminServiceImplTest.testConcurrentDynamicModelDelete (handle InvalidNodeRefException after getChildAssocs)
   20692: Merged BRANCHES/DEV/V3.3-DAO-REFACTOR-4 to BRANCHES/DEV/V3.3-DAO-REFACTOR-5:
        - Retired all 1.3 and 1.4 upgrade scripts ... R.I.P.
        - Fixed CRC patch for Authorities (only tested on MySQL)
        - Fixed SQL patch revision numbers and bumped version schema number up
        20158: Merged BRANCHES/DEV/V3.3-DAO-REFACTOR-3 to BRANCHES/DEV/V3.3-DAO-REFACTOR-4:
             19773: SQL mappings and scripts: SAIL-310, SAIL-304, SAIL-303 and SAIL-347
             19774: Futher fix for SAIL-310: Sequence patch must take into account sequences created for 3.3
             19851: SAIL-371 (SAIL-294) NodeDAO fallout: Fix QName and Namespace read/write handling and bean name in unit test
        20183: Merged DAO3 to DAO4
             19852: SAIL-370: Remove LinkValidation
             19853: SAIL-239 (SAIL-294) Attributes.hbm.xml: Added ability to attach arbitrary property to unique context
             19857: SAIL-373 Fallout from Permissions DAO refactor (SAIL-235)
             19864: SAIL-239 (SAIL-294): Removed AttributeService RMI API
             19865: More SAIL-239 (SAIL-294): Removed AttributeService RMI API
        20208: DAO-refactor implementation of ALF-2712 query improvements
        20209: Merged BRANCHES/DEV/V3.3-DAO-REFACTOR-3 to BRANCHES/DEV/V3.3-DAO-REFACTOR-4:
             20060: Removal of AttributeService for SAIL-239 (SAIL-294)
        20348: SAIL-371 (SAIL-294): Protect collection properties during map insert and retrieval
        20547: SAIL-371 (SAIL-294) Attributes.hbm.xml: implement getAttributes + fixes
        20573: SAIL-371 (SAIL-294): NodeDAO: Fix unit tests and other fallout
        20597: SAIL-239 Attributes.hbm.xml: WCM/AVM locking test fixes (wip)
        20598: SAIL-239 Attributes.hbm.xml: WCM/AVM locking test fixes (wip) - fix AssetServiceImplTest.testSimpleLockFile NPE
        20600: Fix PropertyValueDAOTest.testPropertyValue_Enum (follow-on to r20060 for SAIL-239 - which introduces ENUM prop vals)
        20601: Fix UsageDAOTest.testCreateAndDeleteUsageDeltas NPE (would also affect ContentStoreCleanerScalabilityRunner)
        20603: Fix CMISPropertyServiceTest.* (fallout from r20146 <- r19429 <- Took empty cm:content creation out of FileFolderService#createImpl)
        20604: SAIL-371 (SAIL-294): NodeDAO: Fix unit tests - TransferServiceImplTest.*
        20618: SAIL-371 (SAIL-294): NodeDAO: AuditableAspectTest (fix testCreateNodeWithAuditableProperties_ALF_2565 + add remove aspect test)
        20624: SAIL-371 (SAIL-294): NodeDAO: Fix unit tests - UserUsageTest.*
        20626: Fixed random keys for RuleTrigger NodeRef tracking
        20635: SAIL-371 (SAIL-294): NodeDAO: Fix unit tests - PersonTest.testSplitDuplicates
        20642: SAIL-371 (SAIL-294) DAO: Fixed CacheTest
        20643: Removed must of the 'distribute' target's dependencies.  Not for HEAD
        20645: Follow-on to r20643 (Removed most of the 'distribute' target's dependencies.  Not for HEAD)
        20654: SAIL-371 (SAIL-294): NodeDAO: DMDeploymentTargetTest.* (do not try to remove mandatory aspects)
        20655: SAIL-371 (SAIL-294): NodeDAO: Initial fix for TaggingServiceImplTest.testTagScopeUpdateViaNodePolicies (+ minor test cleanup)
        20657: SAIL-371 (SAIL-294): NodeDAO: Fix unit tests - VersionMigratorTest.testMigrateOneVersion (cm:accessed not returned if null)
        20658: Merged (back merge only - no merge info) BRANCHES/V3.3 to BRANCHES/DEV/V3.3-DAO-REFACTOR-4:
             20090: Dynamic models: minor improvements to DictionaryModelType
             20554: Improvement to model delete validation (investigating intermittent failure of RepoAdminServiceImplTest.testSimpleDynamicModelViaNodeService)
        20662: SAIL-371 (SAIL-294): NodeDAO: Fix unit tests - RecordsManagementAuditServiceImplTest.* (we now ignore attempt to update 'cm:modifier' prop so update 'cm:title' prop instead)
        20666: SAIL-371 (SAIL-294): NodeDAO: Fix unit tests - ADMLuceneTest.*
        20668: SAIL-239 (SAIL-294) - delete WCM locks + tests (follow-on to r20060)
        20674: SAIL-371 (SAIL-294) NodeDAO fallout: Cleaner and additional checks for ContentStoreCleaner
        20675: SAIL-371 (SAIL-294) NodeDAO fallout: Fixed handling of ContentData
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@20693 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
		
	
		
			
				
	
	
		
			1124 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			1124 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| /*
 | |
|  * Copyright (C) 2005-2010 Alfresco Software Limited.
 | |
|  *
 | |
|  * This file is part of Alfresco
 | |
|  *
 | |
|  * Alfresco is free software: you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU Lesser General Public License as published by
 | |
|  * the Free Software Foundation, either version 3 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  *
 | |
|  * Alfresco is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public License
 | |
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| package org.alfresco.repo.domain;
 | |
| 
 | |
| import java.io.ByteArrayInputStream;
 | |
| import java.io.ByteArrayOutputStream;
 | |
| import java.io.ObjectInputStream;
 | |
| import java.io.ObjectOutputStream;
 | |
| import java.io.Serializable;
 | |
| import java.util.ArrayList;
 | |
| import java.util.Collection;
 | |
| import java.util.Collections;
 | |
| import java.util.Date;
 | |
| import java.util.HashMap;
 | |
| import java.util.Locale;
 | |
| import java.util.Map;
 | |
| 
 | |
| import org.alfresco.error.AlfrescoRuntimeException;
 | |
| import org.alfresco.repo.domain.schema.SchemaBootstrap;
 | |
| import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
 | |
| import org.alfresco.service.cmr.repository.AssociationRef;
 | |
| import org.alfresco.service.cmr.repository.ChildAssociationRef;
 | |
| import org.alfresco.service.cmr.repository.ContentData;
 | |
| import org.alfresco.service.cmr.repository.MLText;
 | |
| import org.alfresco.service.cmr.repository.NodeRef;
 | |
| import org.alfresco.service.cmr.repository.Path;
 | |
| import org.alfresco.service.cmr.repository.Period;
 | |
| import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
 | |
| import org.alfresco.service.namespace.QName;
 | |
| import org.alfresco.util.EqualsHelper;
 | |
| import org.alfresco.util.VersionNumber;
 | |
| import org.apache.commons.logging.Log;
 | |
| import org.apache.commons.logging.LogFactory;
 | |
| 
 | |
| /**
 | |
|  * Immutable property value storage class.
 | |
|  * <p>
 | |
|  * <b>As of 2.2.1, this class is only used by the AVM persistence layers.</b>
 | |
|  * 
 | |
|  * @author Derek Hulley
 | |
|  */
 | |
| public class PropertyValue implements Cloneable, Serializable
 | |
| {
 | |
|     private static final long serialVersionUID = -497902497351493075L;
 | |
| 
 | |
|     /** used to take care of empty strings being converted to nulls by the database */
 | |
|     private static final String STRING_EMPTY = "";
 | |
|     
 | |
|     private static Log logger = LogFactory.getLog(PropertyValue.class);
 | |
|     private static Log loggerOracle = LogFactory.getLog(PropertyValue.class.getName() + ".oracle");
 | |
| 
 | |
|     /** potential value types */
 | |
|     private static enum ValueType
 | |
|     {
 | |
|         NULL
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(0);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return null;
 | |
|             }
 | |
|         },
 | |
|         BOOLEAN
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(1);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(Boolean.class, value);
 | |
|             }
 | |
|         },
 | |
|         INTEGER
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(2);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             protected ValueType getPersistedType(Serializable value)
 | |
|             {
 | |
|                 return ValueType.LONG;
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(Integer.class, value);
 | |
|             }
 | |
|         },
 | |
|         LONG
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(3);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(Long.class, value);
 | |
|             }
 | |
|         },
 | |
|         FLOAT
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(4);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(Float.class, value);
 | |
|             }
 | |
|         },
 | |
|         DOUBLE
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(5);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(Double.class, value);
 | |
|             }
 | |
|         },
 | |
|         STRING
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(6);
 | |
|             }
 | |
| 
 | |
|             /**
 | |
|              * Strings longer than the maximum of {@link PropertyValue#DEFAULT_MAX_STRING_LENGTH}
 | |
|              * characters will be serialized.
 | |
|              */
 | |
|             @Override
 | |
|             protected ValueType getPersistedType(Serializable value)
 | |
|             {
 | |
|                 if (value instanceof String)
 | |
|                 {
 | |
|                     String valueStr = (String) value;
 | |
|                     // Check how long the String can be
 | |
|                     if (valueStr.length() > SchemaBootstrap.getMaxStringLength())
 | |
|                     {
 | |
|                         return ValueType.SERIALIZABLE;
 | |
|                     }
 | |
|                 }
 | |
|                 return ValueType.STRING;
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(String.class, value);
 | |
|             }
 | |
|         },
 | |
|         DATE
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(7);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             protected ValueType getPersistedType(Serializable value)
 | |
|             {
 | |
|                 return ValueType.STRING;
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(Date.class, value);
 | |
|             }
 | |
|         },
 | |
|         /**
 | |
|          * @deprecated          column FK to alf_global_attributes has been removed (3.4)
 | |
|          */
 | |
|         DB_ATTRIBUTE
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(8);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return null;
 | |
|             }
 | |
|         },
 | |
|         SERIALIZABLE
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(9);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return value;
 | |
|             }
 | |
|         },
 | |
|         MLTEXT
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(10);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             protected ValueType getPersistedType(Serializable value)
 | |
|             {
 | |
|                 // NOTE: since 2.2.1, PropertyValue is only used by AVM (which does not natively support MLText, other than single/default string)
 | |
|                 return ValueType.STRING;
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 // NOTE: since 2.2.1, PropertyValue is only used by AVM (which does not natively support MLText, other than single/default string)
 | |
|                 MLText mlText = DefaultTypeConverter.INSTANCE.convert(MLText.class, value);
 | |
|                 if (mlText.size() > 1)
 | |
|                 {
 | |
|                     throw new UnsupportedOperationException("PropertyValue MLText is not supported for AVM");
 | |
|                 }
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(String.class, mlText);
 | |
|             }
 | |
|         },
 | |
|         CONTENT
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(11);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             protected ValueType getPersistedType(Serializable value)
 | |
|             {
 | |
|                 return ValueType.STRING;
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(ContentData.class, value);
 | |
|             }
 | |
|         },
 | |
|         NODEREF
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(12);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             protected ValueType getPersistedType(Serializable value)
 | |
|             {
 | |
|                 return ValueType.STRING;
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(NodeRef.class, value);
 | |
|             }
 | |
|         },
 | |
|         CHILD_ASSOC_REF
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(13);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             protected ValueType getPersistedType(Serializable value)
 | |
|             {
 | |
|                 return ValueType.STRING;
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(ChildAssociationRef.class, value);
 | |
|             }
 | |
|         },
 | |
|         ASSOC_REF
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(14);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             protected ValueType getPersistedType(Serializable value)
 | |
|             {
 | |
|                 return ValueType.STRING;
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(AssociationRef.class, value);
 | |
|             }
 | |
|         },
 | |
|         QNAME
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(15);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             protected ValueType getPersistedType(Serializable value)
 | |
|             {
 | |
|                 return ValueType.STRING;
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(QName.class, value);
 | |
|             }
 | |
|         },
 | |
|         PATH
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(16);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             protected ValueType getPersistedType(Serializable value)
 | |
|             {
 | |
|                 return ValueType.SERIALIZABLE;
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(Path.class, value);
 | |
|             }
 | |
|         },
 | |
|         LOCALE
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(17);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             protected ValueType getPersistedType(Serializable value)
 | |
|             {
 | |
|                 return ValueType.STRING;
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(Locale.class, value);
 | |
|             }
 | |
|         },
 | |
|         VERSION_NUMBER
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(18);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             protected ValueType getPersistedType(Serializable value)
 | |
|             {
 | |
|                 return ValueType.STRING;
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(VersionNumber.class, value);
 | |
|             }
 | |
|         },
 | |
|         PERIOD
 | |
|         {
 | |
|             @Override
 | |
|             public Integer getOrdinalNumber()
 | |
|             {
 | |
|                 return Integer.valueOf(20);
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             protected ValueType getPersistedType(Serializable value)
 | |
|             {
 | |
|                 return ValueType.STRING;
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             Serializable convert(Serializable value)
 | |
|             {
 | |
|                 return DefaultTypeConverter.INSTANCE.convert(Period.class, value);
 | |
|             }
 | |
|         };
 | |
|         
 | |
|         /**
 | |
|          * @return      Returns the manually-maintained ordinal number for the value
 | |
|          */
 | |
|         public abstract Integer getOrdinalNumber();
 | |
|         
 | |
|         /**
 | |
|          * Override if the type gets persisted in a different format.
 | |
|          * 
 | |
|          * @param value the actual value that is to be persisted.  May not be null.
 | |
|          */
 | |
|         protected ValueType getPersistedType(Serializable value)
 | |
|         {
 | |
|             return this;
 | |
|         }
 | |
|         
 | |
|         /**
 | |
|          * Converts a value to this type.  The implementation must be able to cope with any legitimate
 | |
|          * source value.
 | |
|          * 
 | |
|          * @see DefaultTypeConverter.INSTANCE#convert(Class, Object)
 | |
|          */
 | |
|         abstract Serializable convert(Serializable value);
 | |
|         
 | |
|         protected ArrayList<Serializable> convert(Collection<?> collection)
 | |
|         {
 | |
|             ArrayList<Serializable> arrayList = new ArrayList<Serializable>(collection.size());
 | |
|             for (Object object : collection)
 | |
|             {
 | |
|                 Serializable newValue = null;
 | |
|                 if (object != null)
 | |
|                 {
 | |
|                     if (!(object instanceof Serializable))
 | |
|                     {
 | |
|                         throw new AlfrescoRuntimeException("Collection values must contain Serializable instances: \n" +
 | |
|                                 "   value type: " + this + "\n" +
 | |
|                                 "   collection: " + collection + "\n" +
 | |
|                                 "   value: " + object);
 | |
|                     }
 | |
|                     Serializable value = (Serializable) object;
 | |
|                     newValue = convert(value);
 | |
|                 }
 | |
|                 arrayList.add(newValue);
 | |
|             }
 | |
|             // done
 | |
|             return arrayList;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Determine the actual value type to aid in more concise persistence.
 | |
|      * 
 | |
|      * @param value the value that is to be persisted
 | |
|      * @return Returns the value type equivalent of the 
 | |
|      */
 | |
|     private static ValueType getActualType(Serializable value)
 | |
|     {
 | |
|         if (value == null)
 | |
|         {
 | |
|             return ValueType.NULL;
 | |
|         }
 | |
|         else if (value instanceof Boolean)
 | |
|         {
 | |
|             return ValueType.BOOLEAN;
 | |
|         }
 | |
|         else if ((value instanceof Integer) || (value instanceof Long))
 | |
|         {
 | |
|             return ValueType.LONG;
 | |
|         }
 | |
|         else if (value instanceof Float)
 | |
|         {
 | |
|             return ValueType.FLOAT;
 | |
|         }
 | |
|         else if (value instanceof Double)
 | |
|         {
 | |
|             return ValueType.DOUBLE;
 | |
|         }
 | |
|         else if (value instanceof String)
 | |
|         {
 | |
|             return ValueType.STRING;
 | |
|         }
 | |
|         else if (value instanceof Date)
 | |
|         {
 | |
|             return ValueType.DATE;
 | |
|         }
 | |
|         else if (value instanceof ContentData)
 | |
|         {
 | |
|             return ValueType.CONTENT;
 | |
|         }
 | |
|         else if (value instanceof NodeRef)
 | |
|         {
 | |
|             return ValueType.NODEREF;
 | |
|         }
 | |
|         else if (value instanceof ChildAssociationRef)
 | |
|         {
 | |
|             return ValueType.CHILD_ASSOC_REF;
 | |
|         }
 | |
|         else if (value instanceof AssociationRef)
 | |
|         {
 | |
|             return ValueType.ASSOC_REF;
 | |
|         }
 | |
|         else if (value instanceof QName)
 | |
|         {
 | |
|             return ValueType.QNAME;
 | |
|         }
 | |
|         else if (value instanceof Path)
 | |
|         {
 | |
|             return ValueType.PATH;
 | |
|         }
 | |
|         else if (value instanceof Locale)
 | |
|         {
 | |
|             return ValueType.LOCALE;
 | |
|         }
 | |
|         else if (value instanceof VersionNumber)
 | |
|         {
 | |
|             return ValueType.VERSION_NUMBER;
 | |
|         }
 | |
|         else if (value instanceof MLText)
 | |
|         {
 | |
|             return ValueType.MLTEXT;
 | |
|         }
 | |
|         else if (value instanceof Period)
 | |
|         {
 | |
|             return ValueType.PERIOD;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             // type is not recognised as belonging to any particular slot
 | |
|             return ValueType.SERIALIZABLE;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /** a mapping from a property type <code>QName</code> to the corresponding value type */
 | |
|     private static Map<QName, ValueType> valueTypesByPropertyType;
 | |
|     /**
 | |
|      * a mapping of {@link ValueType} ordinal number to the enum.  This is manually maintained
 | |
|      * and <b>MUST NOT BE CHANGED FOR EXISTING VALUES</b>.
 | |
|      */
 | |
|     private static Map<Integer, ValueType> valueTypesByOrdinalNumber;
 | |
|     static
 | |
|     {
 | |
|         valueTypesByPropertyType = new HashMap<QName, ValueType>(37);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.ANY, ValueType.SERIALIZABLE);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.BOOLEAN, ValueType.BOOLEAN);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.INT, ValueType.INTEGER);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.LONG, ValueType.LONG);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.DOUBLE, ValueType.DOUBLE);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.FLOAT, ValueType.FLOAT);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.DATE, ValueType.DATE);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.DATETIME, ValueType.DATE);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.CATEGORY, ValueType.NODEREF);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.CONTENT, ValueType.CONTENT);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.TEXT, ValueType.STRING);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.MLTEXT, ValueType.MLTEXT);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.NODE_REF, ValueType.NODEREF);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.CHILD_ASSOC_REF, ValueType.CHILD_ASSOC_REF);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.ASSOC_REF, ValueType.ASSOC_REF);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.PATH, ValueType.PATH);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.QNAME, ValueType.QNAME);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.LOCALE, ValueType.LOCALE);
 | |
|         valueTypesByPropertyType.put(DataTypeDefinition.PERIOD, ValueType.PERIOD);
 | |
|         
 | |
|         valueTypesByOrdinalNumber = new HashMap<Integer, ValueType>(37);
 | |
|         for (ValueType valueType : ValueType.values())
 | |
|         {
 | |
|             Integer ordinalNumber = valueType.getOrdinalNumber();
 | |
|             if (valueTypesByOrdinalNumber.containsKey(ordinalNumber))
 | |
|             {
 | |
|                 throw new RuntimeException("ValueType has duplicate ordinal number: " + valueType);
 | |
|             }
 | |
|             else if (ordinalNumber.intValue() == -1)
 | |
|             {
 | |
|                 throw new RuntimeException("ValueType doesn't have an ordinal number: " + valueType);
 | |
|             }
 | |
|             valueTypesByOrdinalNumber.put(ordinalNumber, valueType);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /** the type of the property, prior to serialization persistence */
 | |
|     private ValueType actualType;
 | |
|     /** true if the property values are contained in a collection */
 | |
|     private boolean isMultiValued;
 | |
|     /** the type of persistence used */
 | |
|     private ValueType persistedType;
 | |
|     
 | |
|     private Boolean booleanValue;
 | |
|     private Long longValue;
 | |
|     private Float floatValue;
 | |
|     private Double doubleValue;
 | |
|     private String stringValue;
 | |
|     private Serializable serializableValue;
 | |
|     
 | |
|     /**
 | |
|      * default constructor
 | |
|      */
 | |
|     public PropertyValue()
 | |
|     {
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Construct a new property value.
 | |
|      * 
 | |
|      * @param typeQName the dictionary-defined property type to store the property as.   
 | |
|      * May be null in which case the type will be determined from the value parameter.
 | |
|      * @param value the value to store.  This will be converted into a format compatible
 | |
|      *      with the type given
 | |
|      * 
 | |
|      * @throws java.lang.UnsupportedOperationException if the value cannot be converted to the
 | |
|      *      type given
 | |
|      */
 | |
|     public PropertyValue(QName typeQName, Serializable value)
 | |
|     {
 | |
|         this.actualType = PropertyValue.getActualType(value);
 | |
|         if (value == null)
 | |
|         {
 | |
|             setPersistedValue(ValueType.NULL, null);
 | |
|             setMultiValued(false);
 | |
|         }
 | |
|         else if (value instanceof Collection<?>)
 | |
|         {
 | |
|             if(typeQName != null)
 | |
|             {  
 | |
|                 Collection<?> collection = (Collection<?>) value;
 | |
|                 ValueType collectionValueType = makeValueType(typeQName);
 | |
|                 // convert the collection values - we need to do this to ensure that the
 | |
|                 // values provided conform to the given type
 | |
|             
 | |
|                 ArrayList<Serializable> convertedCollection = collectionValueType.convert(collection);
 | |
|                 // the persisted type is, nonetheless, a serializable
 | |
|                 setPersistedValue(ValueType.SERIALIZABLE, convertedCollection);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 setPersistedValue(ValueType.SERIALIZABLE, value);
 | |
|             }
 | |
|              
 | |
| 
 | |
|             setMultiValued(true);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             // Does the client consider the type to be important?
 | |
|             if (typeQName != null)
 | |
|             {
 | |
|                 // Convert the value to the type required.  This ensures that any type conversion issues
 | |
|                 // are caught early and prevent the scenario where the data in the DB cannot be given
 | |
|                 // back out because it is unconvertable.
 | |
|                 ValueType valueType = makeValueType(typeQName);
 | |
|                 value = valueType.convert(value);
 | |
|             }
 | |
|             // get the persisted type
 | |
|             ValueType persistedValueType = this.actualType.getPersistedType(value);
 | |
|             // convert to the persistent type
 | |
|             value = persistedValueType.convert(value);
 | |
|             setPersistedValue(persistedValueType, value);
 | |
|             setMultiValued(false);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Helper method to convert the type <code>QName</code> into a <code>ValueType</code>
 | |
|      * 
 | |
|      * @return Returns the <code>ValueType</code>  - never null
 | |
|      */
 | |
|     private static ValueType makeValueType(QName typeQName)
 | |
|     {
 | |
|         ValueType valueType = valueTypesByPropertyType.get(typeQName);
 | |
|         if (valueType == null)
 | |
|         {
 | |
|             throw new AlfrescoRuntimeException(
 | |
|                     "Property type not recognised: \n" +
 | |
|                     "   type: " + typeQName);
 | |
|         }
 | |
|         return valueType;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Given an actual type qualified name, returns the <tt>int</tt> ordinal number
 | |
|      * that represents it in the database.
 | |
|      * 
 | |
|      * @param typeQName the type qualified name
 | |
|      * @return Returns the <tt>int</tt> representation of the type,
 | |
|      *      e.g. <b>CONTENT.getOrdinalNumber()</b> for type <b>d:content</b>.
 | |
|      */
 | |
|     public static int convertToTypeOrdinal(QName typeQName)
 | |
|     {
 | |
|         ValueType valueType = makeValueType(typeQName);
 | |
|         return valueType.getOrdinalNumber();
 | |
|     }
 | |
|     
 | |
|     @Override
 | |
|     public boolean equals(Object obj)
 | |
|     {
 | |
|         if (this == obj)
 | |
|         {
 | |
|             return true;
 | |
|         }
 | |
|         if (obj == null)
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
|         if (obj instanceof PropertyValue)
 | |
|         {
 | |
|             PropertyValue that = (PropertyValue) obj;
 | |
|             return (this.actualType.equals(that.actualType) &&
 | |
|                     EqualsHelper.nullSafeEquals(this.booleanValue, that.booleanValue) &&
 | |
|                     EqualsHelper.nullSafeEquals(this.longValue, that.longValue) &&
 | |
|                     EqualsHelper.nullSafeEquals(this.floatValue, that.floatValue) &&
 | |
|                     EqualsHelper.nullSafeEquals(this.doubleValue, that.doubleValue) &&
 | |
|                     EqualsHelper.nullSafeEquals(this.stringValue, that.stringValue) &&
 | |
|                     EqualsHelper.nullSafeEquals(this.serializableValue, that.serializableValue)
 | |
|                     );
 | |
|             
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     @Override
 | |
|     public int hashCode()
 | |
|     {
 | |
|         int h = 0;
 | |
|         if (actualType != null)
 | |
|             h = actualType.hashCode();
 | |
|         Serializable persistedValue = getPersistedValue();
 | |
|         if (persistedValue != null)
 | |
|             h += 17 * persistedValue.hashCode();
 | |
|         return h;
 | |
|     }
 | |
|     
 | |
|     @Override
 | |
|     public Object clone() throws CloneNotSupportedException
 | |
|     {
 | |
|         return super.clone();
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public String toString()
 | |
|     {
 | |
|         StringBuilder sb = new StringBuilder(128);
 | |
|         sb.append("PropertyValue")
 | |
|           .append("[actual-type=").append(actualType)
 | |
|           .append(", multi-valued=").append(isMultiValued)
 | |
|           .append(", value-type=").append(persistedType)
 | |
|           .append(", value=").append(getPersistedValue())
 | |
|           .append("]");
 | |
|         return sb.toString();
 | |
|     }
 | |
| 
 | |
|     public Integer getActualType()
 | |
|     {
 | |
|         return actualType == null ? null : actualType.getOrdinalNumber();
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @return          Returns the actual type's String representation
 | |
|      */
 | |
|     public String getActualTypeString()
 | |
|     {
 | |
|         return actualType == null ? null : actualType.toString();
 | |
|     }
 | |
| 
 | |
|     public void setActualType(Integer actualType)
 | |
|     {
 | |
|         ValueType type = PropertyValue.valueTypesByOrdinalNumber.get(actualType);
 | |
|         if (type == null)
 | |
|         {
 | |
|             logger.error("Unknown property actual type ordinal number: " + actualType);
 | |
|         }
 | |
|         this.actualType = type;
 | |
|     }
 | |
| 
 | |
|     public boolean isMultiValued()
 | |
|     {
 | |
|         return isMultiValued;
 | |
|     }
 | |
| 
 | |
|     public void setMultiValued(boolean isMultiValued)
 | |
|     {
 | |
|         this.isMultiValued = isMultiValued;
 | |
|     }
 | |
| 
 | |
|     public Integer getPersistedType()
 | |
|     {
 | |
|         return persistedType == null ? null : persistedType.getOrdinalNumber();
 | |
|     }
 | |
|     public void setPersistedType(Integer persistedType)
 | |
|     {
 | |
|         ValueType type = PropertyValue.valueTypesByOrdinalNumber.get(persistedType);
 | |
|         if (type == null)
 | |
|         {
 | |
|             logger.error("Unknown property persisted type ordinal number: " + persistedType);
 | |
|         }
 | |
|         this.persistedType = type;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Stores the value in the correct slot based on the type of persistence requested.
 | |
|      * No conversion is done.
 | |
|      * 
 | |
|      * @param persistedType the value type
 | |
|      * @param value the value - it may only be null if the persisted type is {@link ValueType#NULL}
 | |
|      */
 | |
|     public void setPersistedValue(ValueType persistedType, Serializable value)
 | |
|     {
 | |
|         switch (persistedType)
 | |
|         {
 | |
|             case NULL:
 | |
|                 if (value != null)
 | |
|                 {
 | |
|                     throw new AlfrescoRuntimeException("Value must be null for persisted type: " + persistedType);
 | |
|                 }
 | |
|                 break;
 | |
|             case BOOLEAN:
 | |
|                 this.booleanValue = (Boolean) value;
 | |
|                 break;
 | |
|             case LONG:
 | |
|                 this.longValue = (Long) value;
 | |
|                 break;
 | |
|             case FLOAT:
 | |
|                 this.floatValue = (Float) value;
 | |
|                 break;
 | |
|             case DOUBLE:
 | |
|                 this.doubleValue = (Double) value;
 | |
|                 break;
 | |
|             case STRING:
 | |
|                 this.stringValue = (String) value;
 | |
|                 break;
 | |
|             case DB_ATTRIBUTE:
 | |
|                 throw new IllegalArgumentException("DB_ATTRIBUTE is no longer supported.");
 | |
|             case SERIALIZABLE:
 | |
|                 this.serializableValue = cloneSerializable(value);
 | |
|                 break;
 | |
|             default:
 | |
|                 throw new AlfrescoRuntimeException("Unrecognised value type: " + persistedType);
 | |
|         }
 | |
|         // we store the type that we persisted as
 | |
|         this.persistedType = persistedType;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Clones a serializable object to disconnect the original instance from the persisted instance.
 | |
|      * 
 | |
|      * @param original          the original object
 | |
|      * @return                  the new cloned object
 | |
|      */
 | |
|     private Serializable cloneSerializable(Serializable original)
 | |
|     {
 | |
|        ObjectOutputStream objectOut = null;
 | |
|        ByteArrayOutputStream byteOut = null;
 | |
|        ObjectInputStream objectIn = null;
 | |
|         try
 | |
|         {
 | |
|            // Write the object out to a byte array
 | |
|            byteOut = new ByteArrayOutputStream();
 | |
|            objectOut = new ObjectOutputStream(byteOut);
 | |
|            objectOut.writeObject(original);
 | |
|            objectOut.flush();
 | |
| 
 | |
|            objectIn = new ObjectInputStream(new ByteArrayInputStream(byteOut.toByteArray()));
 | |
|            Object target = objectIn.readObject();
 | |
|            // Done
 | |
|            return (Serializable) target;
 | |
|         }
 | |
|         catch (Throwable e)
 | |
|         {
 | |
|             throw new AlfrescoRuntimeException("Failed to clone serializable object: " + original, e);
 | |
|         }
 | |
|         finally
 | |
|         {
 | |
|            if (objectOut != null)
 | |
|            {
 | |
|               try { objectOut.close(); } catch (Throwable e) {}
 | |
|            }
 | |
|            if (byteOut != null)
 | |
|            {
 | |
|               try { byteOut.close(); } catch (Throwable e) {}
 | |
|            }
 | |
|            if (objectIn != null)
 | |
|            {
 | |
|               try { objectIn.close(); } catch (Throwable e) {}
 | |
|            }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @return Returns the persisted value, keying off the persisted value type
 | |
|      */
 | |
|     private Serializable getPersistedValue()
 | |
|     {
 | |
|         switch (persistedType)
 | |
|         {
 | |
|             case NULL:
 | |
|                 return null;
 | |
|             case BOOLEAN:
 | |
|                 return this.booleanValue;
 | |
|             case LONG:
 | |
|                 return this.longValue;
 | |
|             case FLOAT:
 | |
|                 return this.floatValue;
 | |
|             case DOUBLE:
 | |
|                 return this.doubleValue;
 | |
|             case STRING:
 | |
|                 // Oracle stores empty strings as 'null'...
 | |
|                 if (this.stringValue == null)
 | |
|                 {
 | |
|                     // We know that we stored a non-null string, but now it is null.
 | |
|                     // It can only mean one thing - Oracle
 | |
|                     if (loggerOracle.isDebugEnabled())
 | |
|                     {
 | |
|                         logger.debug("string_value is 'null'.  Forcing to empty String");
 | |
|                     }
 | |
|                     return PropertyValue.STRING_EMPTY;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     return this.stringValue;
 | |
|                 }
 | |
|             case DB_ATTRIBUTE:
 | |
|                 return null;
 | |
|             case SERIALIZABLE:
 | |
|                 return this.serializableValue;
 | |
|             default:
 | |
|                 throw new AlfrescoRuntimeException("Unrecognised value type: " + persistedType);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Fetches the value as a desired type.  Collections (i.e. multi-valued properties)
 | |
|      * will be converted as a whole to ensure that all the values returned within the
 | |
|      * collection match the given type.
 | |
|      * 
 | |
|      * @param typeQName the type required for the return value
 | |
|      * @return Returns the value of this property as the desired type, or a <code>Collection</code>
 | |
|      *      of values of the required type
 | |
|      * 
 | |
|      * @throws AlfrescoRuntimeException
 | |
|      *      if the type given is not recognized
 | |
|      * @throws org.alfresco.service.cmr.repository.datatype.TypeConversionException
 | |
|      *      if the conversion to the required type fails
 | |
|      * 
 | |
|      * @see DataTypeDefinition#ANY The static qualified names for the types
 | |
|      */
 | |
|     public Serializable getValue(QName typeQName)
 | |
|     {
 | |
|         // first check for null
 | |
|         ValueType requiredType = makeValueType(typeQName);
 | |
|         if (requiredType == ValueType.SERIALIZABLE)
 | |
|         {
 | |
|             // the required type must be the actual type
 | |
|             requiredType = this.actualType;
 | |
|         }
 | |
|         
 | |
|         // we need to convert
 | |
|         Serializable ret = null;
 | |
|         if (persistedType == ValueType.NULL)
 | |
|         {
 | |
|             ret = null;
 | |
|         }
 | |
|         else if (this.isMultiValued)
 | |
|         {
 | |
|             // collections are always stored
 | |
|             Collection<?> collection = (Collection<?>) this.serializableValue;
 | |
|             // convert the collection values - we need to do this to ensure that the
 | |
|             // values provided conform to the given type
 | |
|             ArrayList<Serializable> convertedCollection = requiredType.convert(collection);
 | |
|             ret = convertedCollection;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             Serializable persistedValue = getPersistedValue();
 | |
|             // convert the type
 | |
|             ret = requiredType.convert(persistedValue);
 | |
|         }
 | |
|         // done
 | |
|         if (logger.isDebugEnabled())
 | |
|         {
 | |
|             logger.debug("Fetched value: \n" +
 | |
|                     "   property value: " + this + "\n" +
 | |
|                     "   requested type: " + requiredType + "\n" +
 | |
|                     "   result: " + ret);
 | |
|         }
 | |
|         return ret;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Gets the value or values as a guaranteed collection.
 | |
|      * 
 | |
|      * @see #getValue(QName)
 | |
|      */
 | |
|     @SuppressWarnings("unchecked")
 | |
|     public Collection<Serializable> getCollection(QName typeQName)
 | |
|     {
 | |
|         Serializable value = getValue(typeQName);
 | |
|         if (value instanceof Collection<?>)
 | |
|         {
 | |
|             return (Collection<Serializable>) value;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             return Collections.singletonList(value);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     public boolean getBooleanValue()
 | |
|     {
 | |
|         if (booleanValue == null)
 | |
|             return false;
 | |
|         else
 | |
|             return booleanValue.booleanValue();
 | |
|     }
 | |
|     public void setBooleanValue(boolean value)
 | |
|     {
 | |
|         this.booleanValue = Boolean.valueOf(value);
 | |
|     }
 | |
|     
 | |
|     public long getLongValue()
 | |
|     {
 | |
|         if (longValue == null)
 | |
|             return 0;
 | |
|         else
 | |
|             return longValue.longValue();
 | |
|     }
 | |
|     public void setLongValue(long value)
 | |
|     {
 | |
|         this.longValue = Long.valueOf(value);
 | |
|     }
 | |
|     
 | |
|     public float getFloatValue()
 | |
|     {
 | |
|         if (floatValue == null)
 | |
|             return 0.0F;
 | |
|         else
 | |
|             return floatValue.floatValue();
 | |
|     }
 | |
|     public void setFloatValue(float value)
 | |
|     {
 | |
|         this.floatValue = Float.valueOf(value);
 | |
|     }
 | |
|     
 | |
|     public double getDoubleValue()
 | |
|     {
 | |
|         if (doubleValue == null)
 | |
|             return 0.0;
 | |
|         else
 | |
|             return doubleValue.doubleValue();
 | |
|     }
 | |
|     public void setDoubleValue(double value)
 | |
|     {
 | |
|         this.doubleValue = Double.valueOf(value);
 | |
|     }
 | |
|     
 | |
|     public String getStringValue()
 | |
|     {
 | |
|         return stringValue;
 | |
|     }
 | |
|     public void setStringValue(String value)
 | |
|     {
 | |
|         this.stringValue = value;
 | |
|     }
 | |
|     
 | |
|     public Serializable getSerializableValue()
 | |
|     {
 | |
|         return serializableValue;
 | |
|     }
 | |
|     public void setSerializableValue(Serializable value)
 | |
|     {
 | |
|         this.serializableValue = value;
 | |
|     }
 | |
| }
 |