From 27a75808f14de41154b7a817599b004d88d86e8d Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Thu, 15 Sep 2016 17:16:43 +0100 Subject: [PATCH] Transfer code from svn to gitlab.alfresco.com --- pom.xml | 252 + .../org/alfresco/encoding/TikaCharsetFinder.java | 95 + src/main/java/org/alfresco/model/ContentModel.java | 470 + .../java/org/alfresco/model/RenditionModel.java | 49 + .../opencmis/CMISAccessControlFormatEnum.java | 71 + src/main/java/org/alfresco/opencmis/CMISUtils.java | 169 + .../java/org/alfresco/opencmis/EnumFactory.java | 151 + src/main/java/org/alfresco/opencmis/EnumLabel.java | 41 + .../dictionary/AbstractTypeDefinitionWrapper.java | 813 + .../dictionary/BasePropertyDefintionWrapper.java | 89 + .../dictionary/CMISAbstractDictionaryService.java | 507 + .../opencmis/dictionary/CMISActionEvaluator.java | 46 + .../dictionary/CMISDictionaryRegistry.java | 63 + .../dictionary/CMISDictionaryRegistryImpl.java | 538 + .../opencmis/dictionary/CMISDictionaryReload.java | 77 + .../opencmis/dictionary/CMISDictionaryService.java | 103 + .../alfresco/opencmis/dictionary/CMISNodeInfo.java | 104 + .../opencmis/dictionary/CMISObjectVariant.java | 41 + .../opencmis/dictionary/CMISPropertyAccessor.java | 80 + .../dictionary/CMISPropertyLuceneBuilder.java | 130 + .../dictionary/CMISStrictDictionaryService.java | 319 + .../dictionary/DocumentTypeDefinitionWrapper.java | 141 + .../dictionary/FilteredDictionaryComponent.java | 93 + .../dictionary/FolderTypeDefintionWrapper.java | 147 + .../dictionary/ItemTypeDefinitionWrapper.java | 149 + .../dictionary/PolicyTypeDefintionWrapper.java | 277 + .../dictionary/PropertyAccessorMapping.java | 65 + .../dictionary/PropertyDefinitionWrapper.java | 44 + .../dictionary/PropertyLuceneBuilderMapping.java | 52 + .../alfresco/opencmis/dictionary/QNameFilter.java | 67 + .../opencmis/dictionary/QNameFilterImpl.java | 192 + .../RelationshipTypeDefintionWrapper.java | 272 + .../dictionary/SecondaryTypeDefinitionWrapper.java | 272 + .../dictionary/ShadowTypeDefinitionWrapper.java | 137 + .../opencmis/dictionary/TypeDefinitionWrapper.java | 69 + .../mapping/AbstractSimpleLuceneBuilder.java | 138 + .../opencmis/mapping/BaseLuceneBuilder.java | 129 + .../opencmis/mapping/BaseTypeIdLuceneBuilder.java | 100 + .../org/alfresco/opencmis/mapping/CMISMapping.java | 860 + .../mapping/ContentStreamLengthLuceneBuilder.java | 85 + .../ContentStreamMimetypeLuceneBuilder.java | 85 + .../opencmis/mapping/DirectLuceneBuilder.java | 143 + .../opencmis/mapping/FixedValueLuceneBuilder.java | 269 + .../mapping/NotSupportedLuceneBuilder.java | 42 + .../opencmis/mapping/ObjectIdLuceneBuilder.java | 168 + .../mapping/ObjectTypeIdLuceneBuilder.java | 154 + .../opencmis/mapping/ParentLuceneBuilder.java | 135 + .../RuntimePropertyLuceneBuilderMapping.java | 127 + .../opencmis/search/CMISFTSQueryParser.java | 424 + .../alfresco/opencmis/search/CMISQueryOptions.java | 193 + .../alfresco/opencmis/search/CMISQueryParser.java | 1718 ++ .../search/CmisFunctionEvaluationContext.java | 552 + .../java/org/alfresco/repo/cache/LockingCache.java | 61 + .../java/org/alfresco/repo/cache/MemoryCache.java | 79 + .../java/org/alfresco/repo/cache/SimpleCache.java | 93 + .../org/alfresco/repo/content/ContentContext.java | 92 + .../org/alfresco/repo/content/ContentStore.java | 284 + .../alfresco/repo/content/ContentStoreCaps.java | 59 + .../org/alfresco/repo/content/MimetypeMap.java | 935 + .../content/encoding/ContentCharsetFinder.java | 117 + .../dictionary/AbstractDictionaryRegistry.java | 726 + .../alfresco/repo/dictionary/CompiledModel.java | 459 + .../repo/dictionary/CompiledModelsCache.java | 106 + .../dictionary/CoreDictionaryRegistryImpl.java | 128 + .../repo/dictionary/CustomModelDefinitionImpl.java | 215 + .../alfresco/repo/dictionary/CustomModelsInfo.java | 69 + .../repo/dictionary/DelegateModelQuery.java | 173 + .../repo/dictionary/DictionaryBootstrap.java | 232 + .../repo/dictionary/DictionaryComponent.java | 548 + .../alfresco/repo/dictionary/DictionaryDAO.java | 297 + .../repo/dictionary/DictionaryDAOImpl.java | 1054 + .../repo/dictionary/DictionaryListener.java | 50 + .../dictionary/DictionaryNamespaceComponent.java | 111 + .../repo/dictionary/DictionaryRegistry.java | 82 + .../dictionary/ExtendedDictionaryListener.java | 39 + .../org/alfresco/repo/dictionary/Facetable.java | 67 + .../repo/dictionary/IndexTokenisationMode.java | 70 + .../repo/dictionary/M2AnonymousTypeDefinition.java | 255 + .../org/alfresco/repo/dictionary/M2Aspect.java | 41 + .../repo/dictionary/M2AspectDefinition.java | 80 + .../alfresco/repo/dictionary/M2Association.java | 46 + .../repo/dictionary/M2AssociationDefinition.java | 356 + .../repo/dictionary/M2ChildAssociation.java | 85 + .../dictionary/M2ChildAssociationDefinition.java | 70 + .../java/org/alfresco/repo/dictionary/M2Class.java | 304 + .../repo/dictionary/M2ClassAssociation.java | 219 + .../repo/dictionary/M2ClassDefinition.java | 781 + .../org/alfresco/repo/dictionary/M2Constraint.java | 139 + .../repo/dictionary/M2ConstraintDefinition.java | 523 + .../org/alfresco/repo/dictionary/M2DataType.java | 125 + .../repo/dictionary/M2DataTypeDefinition.java | 253 + .../java/org/alfresco/repo/dictionary/M2Label.java | 91 + .../java/org/alfresco/repo/dictionary/M2Model.java | 505 + .../repo/dictionary/M2ModelDefinition.java | 196 + .../org/alfresco/repo/dictionary/M2ModelDiff.java | 131 + .../org/alfresco/repo/dictionary/M2NamedValue.java | 98 + .../org/alfresco/repo/dictionary/M2Namespace.java | 69 + .../repo/dictionary/M2NamespaceDefinition.java | 115 + .../org/alfresco/repo/dictionary/M2Property.java | 334 + .../repo/dictionary/M2PropertyDefinition.java | 783 + .../repo/dictionary/M2PropertyOverride.java | 110 + .../java/org/alfresco/repo/dictionary/M2Type.java | 41 + .../alfresco/repo/dictionary/M2TypeDefinition.java | 79 + .../java/org/alfresco/repo/dictionary/M2XML.java | 102 + .../org/alfresco/repo/dictionary/ModelQuery.java | 110 + .../org/alfresco/repo/dictionary/NamespaceDAO.java | 90 + .../dictionary/TenantDictionaryRegistryImpl.java | 259 + .../dictionary/constraint/AbstractConstraint.java | 210 + .../dictionary/constraint/ConstraintRegistry.java | 94 + .../constraint/ListOfValuesConstraint.java | 301 + .../repo/dictionary/constraint/NameChecker.java | 102 + .../repo/dictionary/constraint/NoOpConstraint.java | 41 + .../constraint/NumericRangeConstraint.java | 156 + .../dictionary/constraint/RegexConstraint.java | 162 + .../constraint/RegisteredConstraint.java | 133 + .../constraint/StringLengthConstraint.java | 155 + .../org/alfresco/repo/dictionary/m2binding.xml | 182 + .../alfresco/repo/i18n/StaticMessageLookup.java | 84 + .../java/org/alfresco/repo/index/shard/Floc.java | 230 + .../java/org/alfresco/repo/index/shard/Shard.java | 128 + .../alfresco/repo/index/shard/ShardInstance.java | 187 + .../alfresco/repo/index/shard/ShardMethodEnum.java | 78 + .../org/alfresco/repo/index/shard/ShardState.java | 209 + .../repo/index/shard/ShardStateBuilder.java | 357 + .../org/alfresco/repo/search/MLAnalysisMode.java | 931 + .../repo/search/adaptor/lucene/AnalysisMode.java | 45 + .../repo/search/adaptor/lucene/LuceneFunction.java | 52 + .../adaptor/lucene/LuceneQueryParserAdaptor.java | 235 + .../lucene/LuceneQueryParserExpressionAdaptor.java | 42 + .../repo/search/adaptor/lucene/QueryConstants.java | 205 + .../repo/search/impl/QueryParserUtils.java | 394 + .../parsers/AlfrescoFunctionEvaluationContext.java | 549 + .../org/alfresco/repo/search/impl/parsers/CMIS.g | 1670 ++ .../alfresco/repo/search/impl/parsers/CMIS.tokens | 86 + .../repo/search/impl/parsers/CMISLexer.java | 3418 +++ .../repo/search/impl/parsers/CMISParser.java | 13850 ++++++++++ .../alfresco/repo/search/impl/parsers/CMIS_FTS.g | 433 + .../repo/search/impl/parsers/CMIS_FTS.tokens | 14 + .../repo/search/impl/parsers/CMIS_FTSLexer.java | 592 + .../repo/search/impl/parsers/CMIS_FTSParser.java | 994 + .../org/alfresco/repo/search/impl/parsers/FTS.g | 2426 ++ .../alfresco/repo/search/impl/parsers/FTS.tokens | 104 + .../repo/search/impl/parsers/FTSLexer.java | 18384 +++++++++++++ .../repo/search/impl/parsers/FTSParser.java | 26411 +++++++++++++++++++ .../search/impl/parsers/FTSQueryException.java | 62 + .../repo/search/impl/parsers/FTSQueryParser.java | 1686 ++ .../search/impl/parsers/GenerateUnicodeRanges.java | 129 + .../repo/search/impl/querymodel/Argument.java | 45 + .../search/impl/querymodel/ArgumentDefinition.java | 43 + .../repo/search/impl/querymodel/Column.java | 46 + .../repo/search/impl/querymodel/Conjunction.java | 40 + .../repo/search/impl/querymodel/Constraint.java | 53 + .../repo/search/impl/querymodel/Disjunction.java | 41 + .../search/impl/querymodel/DynamicArgument.java | 37 + .../repo/search/impl/querymodel/Function.java | 75 + .../search/impl/querymodel/FunctionArgument.java | 35 + .../impl/querymodel/FunctionEvaluationContext.java | 221 + .../search/impl/querymodel/FunctionInvokation.java | 48 + .../impl/querymodel/FunctionalConstraint.java | 37 + .../alfresco/repo/search/impl/querymodel/Join.java | 58 + .../repo/search/impl/querymodel/JoinType.java | 46 + .../repo/search/impl/querymodel/ListArgument.java | 37 + .../search/impl/querymodel/LiteralArgument.java | 36 + .../repo/search/impl/querymodel/Multiplicity.java | 34 + .../repo/search/impl/querymodel/Order.java | 35 + .../repo/search/impl/querymodel/Ordering.java | 37 + .../search/impl/querymodel/ParameterArgument.java | 34 + .../repo/search/impl/querymodel/PredicateMode.java | 35 + .../search/impl/querymodel/PropertyArgument.java | 37 + .../repo/search/impl/querymodel/Query.java | 69 + .../repo/search/impl/querymodel/QueryEngine.java | 37 + .../search/impl/querymodel/QueryEngineResults.java | 51 + .../impl/querymodel/QueryModelException.java | 82 + .../search/impl/querymodel/QueryModelFactory.java | 69 + .../repo/search/impl/querymodel/QueryOptions.java | 554 + .../repo/search/impl/querymodel/Selector.java | 49 + .../search/impl/querymodel/SelectorArgument.java | 36 + .../repo/search/impl/querymodel/Source.java | 43 + .../search/impl/querymodel/StaticArgument.java | 35 + .../search/impl/querymodel/impl/BaseArgument.java | 67 + .../querymodel/impl/BaseArgumentDefinition.java | 135 + .../search/impl/querymodel/impl/BaseColumn.java | 112 + .../impl/querymodel/impl/BaseComparison.java | 249 + .../impl/querymodel/impl/BaseConjunction.java | 71 + .../impl/querymodel/impl/BaseConstraint.java | 62 + .../impl/querymodel/impl/BaseDisjunction.java | 70 + .../impl/querymodel/impl/BaseDynamicArgument.java | 47 + .../search/impl/querymodel/impl/BaseFunction.java | 106 + .../impl/querymodel/impl/BaseFunctionArgument.java | 101 + .../querymodel/impl/BaseFunctionalConstraint.java | 83 + .../repo/search/impl/querymodel/impl/BaseJoin.java | 250 + .../impl/querymodel/impl/BaseListArgument.java | 97 + .../impl/querymodel/impl/BaseLiteralArgument.java | 78 + .../search/impl/querymodel/impl/BaseOrdering.java | 74 + .../querymodel/impl/BaseParameterArgument.java | 77 + .../impl/querymodel/impl/BasePropertyArgument.java | 100 + .../search/impl/querymodel/impl/BaseQuery.java | 109 + .../search/impl/querymodel/impl/BaseSelector.java | 116 + .../impl/querymodel/impl/BaseSelectorArgument.java | 81 + .../impl/querymodel/impl/BaseStaticArgument.java | 48 + .../impl/DuplicateSelectorNameException.java | 82 + .../impl/querymodel/impl/SimpleConstraint.java | 49 + .../impl/querymodel/impl/functions/Child.java | 92 + .../impl/querymodel/impl/functions/Descendant.java | 75 + .../impl/querymodel/impl/functions/Equals.java | 57 + .../impl/querymodel/impl/functions/Exists.java | 75 + .../querymodel/impl/functions/FTSFuzzyTerm.java | 74 + .../impl/querymodel/impl/functions/FTSPhrase.java | 81 + .../querymodel/impl/functions/FTSPrefixTerm.java | 71 + .../querymodel/impl/functions/FTSProximity.java | 72 + .../impl/querymodel/impl/functions/FTSRange.java | 77 + .../impl/querymodel/impl/functions/FTSTerm.java | 77 + .../querymodel/impl/functions/FTSWildTerm.java | 71 + .../querymodel/impl/functions/GreaterThan.java | 57 + .../impl/functions/GreaterThanOrEquals.java | 57 + .../search/impl/querymodel/impl/functions/In.java | 80 + .../impl/querymodel/impl/functions/LessThan.java | 58 + .../impl/functions/LessThanOrEquals.java | 58 + .../impl/querymodel/impl/functions/Like.java | 78 + .../impl/querymodel/impl/functions/Lower.java | 76 + .../impl/querymodel/impl/functions/NotEquals.java | 58 + .../impl/functions/PropertyAccessor.java | 79 + .../impl/querymodel/impl/functions/Score.java | 80 + .../impl/querymodel/impl/functions/Upper.java | 76 + .../impl/querymodel/impl/lucene/LuceneColumn.java | 50 + .../querymodel/impl/lucene/LuceneConjunction.java | 106 + .../querymodel/impl/lucene/LuceneDisjunction.java | 100 + .../impl/lucene/LuceneFunctionArgument.java | 50 + .../impl/lucene/LuceneFunctionalConstraint.java | 73 + .../impl/querymodel/impl/lucene/LuceneJoin.java | 52 + .../querymodel/impl/lucene/LuceneListArgument.java | 49 + .../impl/lucene/LuceneLiteralArgument.java | 50 + .../querymodel/impl/lucene/LuceneOrdering.java | 49 + .../impl/lucene/LuceneParameterArgument.java | 47 + .../impl/lucene/LucenePropertyArgument.java | 49 + .../impl/querymodel/impl/lucene/LuceneQuery.java | 198 + .../querymodel/impl/lucene/LuceneQueryBuilder.java | 66 + .../impl/lucene/LuceneQueryBuilderComponent.java | 46 + .../impl/lucene/LuceneQueryBuilderContext.java | 48 + .../impl/lucene/LuceneQueryModelFactory.java | 318 + .../querymodel/impl/lucene/LuceneSelector.java | 64 + .../impl/lucene/LuceneSelectorArgument.java | 47 + .../impl/lucene/functions/LuceneChild.java | 114 + .../impl/lucene/functions/LuceneDescendant.java | 134 + .../impl/lucene/functions/LuceneEquals.java | 77 + .../impl/lucene/functions/LuceneExists.java | 72 + .../impl/lucene/functions/LuceneFTSFuzzyTerm.java | 87 + .../impl/lucene/functions/LuceneFTSPhrase.java | 92 + .../impl/lucene/functions/LuceneFTSPrefixTerm.java | 89 + .../impl/lucene/functions/LuceneFTSProximity.java | 101 + .../impl/lucene/functions/LuceneFTSRange.java | 89 + .../impl/lucene/functions/LuceneFTSTerm.java | 85 + .../impl/lucene/functions/LuceneFTSWildTerm.java | 89 + .../impl/lucene/functions/LuceneGreaterThan.java | 77 + .../functions/LuceneGreaterThanOrEquals.java | 79 + .../querymodel/impl/lucene/functions/LuceneIn.java | 90 + .../impl/lucene/functions/LuceneLessThan.java | 79 + .../lucene/functions/LuceneLessThanOrEquals.java | 79 + .../impl/lucene/functions/LuceneLike.java | 85 + .../impl/lucene/functions/LuceneLower.java | 63 + .../impl/lucene/functions/LuceneNotEquals.java | 78 + .../lucene/functions/LucenePropertyAccessor.java | 59 + .../impl/lucene/functions/LuceneScore.java | 61 + .../impl/lucene/functions/LuceneUpper.java | 60 + .../authentication/AlfrescoSecureContext.java | 79 + .../authentication/AlfrescoSecureContextImpl.java | 157 + .../authentication/AuthenticationDiagnostic.java | 160 + .../authentication/AuthenticationException.java | 124 + .../authentication/AuthenticationStep.java | 53 + .../authentication/AuthenticationUtil.java | 724 + .../InMemoryTicketComponentImpl.java | 574 + .../security/authentication/TicketComponent.java | 150 + .../authentication/TicketExpiredException.java | 49 + .../alfresco/repo/tenant/SingleTServiceImpl.java | 212 + src/main/java/org/alfresco/repo/tenant/Tenant.java | 102 + .../alfresco/repo/tenant/TenantContextHolder.java | 89 + .../org/alfresco/repo/tenant/TenantDeployer.java | 49 + .../org/alfresco/repo/tenant/TenantService.java | 175 + .../alfresco/repo/tenant/TenantUserService.java | 64 + .../java/org/alfresco/repo/tenant/TenantUtil.java | 175 + src/main/java/org/alfresco/service/Auditable.java | 69 + .../java/org/alfresco/service/NotAuditable.java | 49 + .../java/org/alfresco/service/PublicService.java | 44 + .../service/cmr/dictionary/AspectDefinition.java | 39 + .../cmr/dictionary/AssociationDefinition.java | 144 + .../cmr/dictionary/ChildAssociationDefinition.java | 54 + .../cmr/dictionary/ClassAttributeDefinition.java | 67 + .../service/cmr/dictionary/ClassDefinition.java | 165 + .../service/cmr/dictionary/Constraint.java | 94 + .../cmr/dictionary/ConstraintDefinition.java | 83 + .../cmr/dictionary/ConstraintException.java | 43 + .../cmr/dictionary/CustomModelDefinition.java | 71 + .../cmr/dictionary/CustomModelException.java | 196 + .../service/cmr/dictionary/CustomModelService.java | 224 + .../service/cmr/dictionary/DataTypeDefinition.java | 130 + .../cmr/dictionary/DictionaryException.java | 92 + .../service/cmr/dictionary/DictionaryService.java | 327 + .../cmr/dictionary/InvalidAspectException.java | 58 + .../cmr/dictionary/InvalidClassException.java | 60 + .../cmr/dictionary/InvalidTypeException.java | 59 + .../service/cmr/dictionary/ModelDefinition.java | 118 + .../cmr/dictionary/NamespaceDefinition.java | 51 + .../service/cmr/dictionary/PropertyDefinition.java | 182 + .../service/cmr/dictionary/TypeDefinition.java | 40 + .../alfresco/service/cmr/i18n/MessageLookup.java | 86 + .../cmr/repository/AbstractStoreException.java | 70 + .../cmr/repository/AssociationExistsException.java | 85 + .../service/cmr/repository/AssociationRef.java | 213 + .../cmr/repository/ChildAssociationRef.java | 273 + .../service/cmr/repository/ContentAccessor.java | 140 + .../service/cmr/repository/ContentData.java | 379 + .../service/cmr/repository/ContentIOException.java | 54 + .../service/cmr/repository/ContentReader.java | 194 + .../ContentServiceTransientException.java | 56 + .../cmr/repository/ContentStreamListener.java | 45 + .../service/cmr/repository/ContentUrlKey.java | 110 + .../service/cmr/repository/ContentWriter.java | 195 + .../alfresco/service/cmr/repository/EntityRef.java | 45 + .../service/cmr/repository/FileContentReader.java | 52 + .../InvalidChildAssociationRefException.java | 58 + .../cmr/repository/InvalidNodeRefException.java | 66 + .../cmr/repository/InvalidStoreRefException.java | 50 + .../alfresco/service/cmr/repository/MLText.java | 200 + .../cmr/repository/MalformedNodeRefException.java | 63 + .../service/cmr/repository/MimetypeService.java | 182 + .../cmr/repository/MimetypeServiceAware.java | 36 + .../alfresco/service/cmr/repository/NodeRef.java | 311 + .../service/cmr/repository/NodeService.java | 998 + .../org/alfresco/service/cmr/repository/Path.java | 677 + .../alfresco/service/cmr/repository/Period.java | 189 + .../service/cmr/repository/PeriodProvider.java | 97 + .../cmr/repository/StoreExistsException.java | 45 + .../alfresco/service/cmr/repository/StoreRef.java | 129 + .../service/cmr/repository/XPathException.java | 43 + .../repository/datatype/DefaultTypeConverter.java | 843 + .../service/cmr/repository/datatype/Duration.java | 1094 + .../datatype/TypeConversionException.java | 50 + .../cmr/repository/datatype/TypeConverter.java | 614 + .../service/cmr/search/BasicSearchParameters.java | 49 + .../org/alfresco/service/cmr/search/LimitBy.java | 52 + .../cmr/search/NamedQueryParameterDefinition.java | 49 + .../cmr/search/PermissionEvaluationMode.java | 39 + .../service/cmr/search/QueryConsistency.java | 35 + .../service/cmr/search/QueryParameter.java | 66 + .../cmr/search/QueryParameterDefinition.java | 67 + .../org/alfresco/service/cmr/search/ResultSet.java | 47 + .../service/cmr/search/ResultSetColumn.java | 56 + .../service/cmr/search/ResultSetMetaData.java | 111 + .../alfresco/service/cmr/search/ResultSetRow.java | 140 + .../alfresco/service/cmr/search/ResultSetSPI.java | 197 + .../service/cmr/search/ResultSetSelector.java | 49 + .../alfresco/service/cmr/search/ResultSetType.java | 43 + .../service/cmr/search/SearchParameters.java | 1453 + .../alfresco/service/cmr/search/SearchService.java | 273 + .../service/cmr/search/SpellCheckResult.java | 84 + .../service/cmr/search/StatsParameters.java | 159 + .../service/cmr/search/StatsProcessor.java | 38 + .../service/cmr/search/StatsProcessorUsingMap.java | 73 + .../service/cmr/search/StatsResultSet.java | 43 + .../service/cmr/search/StatsResultStat.java | 101 + .../alfresco/service/cmr/search/StatsService.java | 42 + .../service/cmr/security/AccessPermission.java | 90 + .../service/cmr/security/AccessStatus.java | 39 + .../service/cmr/security/AuthorityType.java | 625 + .../service/cmr/security/PermissionContext.java | 105 + .../service/cmr/security/PermissionService.java | 368 + .../namespace/DynamicNamespacePrefixResolver.java | 145 + .../service/namespace/InvalidQNameException.java | 44 + .../service/namespace/NamespaceException.java | 44 + .../service/namespace/NamespacePrefixResolver.java | 81 + .../namespace/NamespacePrefixResolverProvider.java | 45 + .../service/namespace/NamespaceService.java | 168 + .../namespace/NamespaceServiceMemoryImpl.java | 75 + .../java/org/alfresco/service/namespace/QName.java | 528 + .../org/alfresco/service/namespace/QNameCache.java | 140 + .../org/alfresco/service/namespace/QNameMap.java | 219 + .../alfresco/service/namespace/QNamePattern.java | 52 + .../service/namespace/RegexQNamePattern.java | 149 + .../util/BaseApplicationContextHelper.java | 304 + .../DataModelTestApplicationContextHelper.java | 52 + src/main/java/org/alfresco/util/ISO9075.java | 399 + .../util/LazyClassPathXmlApplicationContext.java | 75 + .../NoAutoStartClassPathXmlApplicationContext.java | 99 + .../java/org/alfresco/util/NumericEncoder.java | 247 + .../alfresco/util/SearchLanguageConversion.java | 395 + .../alfresco/util/WebApplicationContextLoader.java | 143 + .../java/org/apache/tika/mime/custom-mimetypes.xml | 28 + .../alfresco/data-model-stand-alone-context.xml | 195 + .../messages/dictionary-messages.properties | 90 + .../alfresco/messages/dictionary-model.properties | 34 + .../messages/dictionary-model_de.properties | 34 + .../messages/dictionary-model_es.properties | 34 + .../messages/dictionary-model_fr.properties | 34 + .../messages/dictionary-model_it.properties | 34 + .../messages/dictionary-model_ja.properties | 34 + .../messages/dictionary-model_nb.properties | 34 + .../messages/dictionary-model_nl.properties | 34 + .../messages/dictionary-model_pt_BR.properties | 34 + .../messages/dictionary-model_ru.properties | 34 + .../messages/dictionary-model_zh_CN.properties | 34 + .../alfresco/mimetype/mimetype-map-openoffice.xml | 101 + .../resources/alfresco/mimetype/mimetype-map.xml | 623 + src/main/resources/alfresco/model/cmisModel.xml | 439 + .../alfresco/model/dataTypeAnalyzers.properties | 18 + .../alfresco/model/dataTypeAnalyzers_cs.properties | 4 + .../alfresco/model/dataTypeAnalyzers_da.properties | 4 + .../alfresco/model/dataTypeAnalyzers_de.properties | 4 + .../alfresco/model/dataTypeAnalyzers_el.properties | 4 + .../alfresco/model/dataTypeAnalyzers_en.properties | 4 + .../alfresco/model/dataTypeAnalyzers_es.properties | 4 + .../alfresco/model/dataTypeAnalyzers_fi.properties | 4 + .../alfresco/model/dataTypeAnalyzers_fr.properties | 4 + .../alfresco/model/dataTypeAnalyzers_it.properties | 4 + .../alfresco/model/dataTypeAnalyzers_ja.properties | 4 + .../alfresco/model/dataTypeAnalyzers_ko.properties | 4 + .../alfresco/model/dataTypeAnalyzers_nl.properties | 4 + .../alfresco/model/dataTypeAnalyzers_no.properties | 4 + .../alfresco/model/dataTypeAnalyzers_pt.properties | 4 + .../model/dataTypeAnalyzers_pt_BR.properties | 4 + .../alfresco/model/dataTypeAnalyzers_ru.properties | 4 + .../alfresco/model/dataTypeAnalyzers_sv.properties | 4 + .../alfresco/model/dataTypeAnalyzers_th.properties | 4 + .../alfresco/model/dataTypeAnalyzers_zh.properties | 4 + .../resources/alfresco/model/dictionaryModel.xml | 132 + .../CMISAbstractDictionaryServiceTest.java | 110 + .../alfresco/repo/cache/SimpleCacheTestBase.java | 129 + .../repo/content/DataModelContentTestSuite.java | 51 + .../org/alfresco/repo/content/MimetypeMapTest.java | 583 + .../repo/content/encoding/CharsetFinderTest.java | 121 + .../repo/dictionary/AbstractModelTest.java | 956 + .../repo/dictionary/DictionaryComponentTest.java | 162 + .../repo/dictionary/DictionaryDAOTest.java | 1167 + .../alfresco/repo/dictionary/DiffModelTest.java | 508 + .../dictionary/constraint/ConstraintsTest.java | 363 + .../alfresco/repo/index/ShardMethodEnumTest.java | 86 + .../repo/search/impl/parsers/CMISTest.java | 89 + .../repo/search/impl/parsers/CMIS_FTSTest.java | 105 + .../alfresco/repo/search/impl/parsers/FTSTest.java | 173 + .../repo/search/impl/parsers/gUnitExecutor.java | 341 + .../service/cmr/repository/MLTextTest.java | 60 + .../service/cmr/repository/NodeRefTest.java | 71 + .../alfresco/service/cmr/repository/PathTest.java | 120 + .../datatype/DefaultTypeConverterTest.java | 350 + .../service/cmr/search/StatsProcessorTest.java | 120 + .../namespace/DynamicNameSpaceResolverTest.java | 121 + .../service/namespace/QNamePatternTest.java | 80 + .../org/alfresco/service/namespace/QNameTest.java | 282 + src/test/java/org/alfresco/util/ISO9075Test.java | 151 + .../org/alfresco/util/NumericEncodingTest.java | 222 + .../util/SearchLanguageConversionTest.java | 161 + .../dictionary/commonpropertynschild_model.xml | 31 + .../dictionary/commonpropertynsparent_model.xml | 12 + .../dictionary/dictionarydaotest_model.properties | 22 + .../repo/dictionary/dictionarydaotest_model.xml | 366 + .../dictionary/dictionarydaotest_model1.properties | 21 + .../repo/dictionary/dictionarydaotest_model1.xml | 84 + .../repo/dictionary/modelCircularTypes.xml | 19 + .../org/alfresco/repo/dictionary/nstest_model.xml | 31 + .../repo/search/impl/parsers/cmis_fts_test.gunit | 83 + .../repo/search/impl/parsers/cmis_test.gunit | 556 + .../repo/search/impl/parsers/fts_test.gunit | 496 + 461 files changed, 140356 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/org/alfresco/encoding/TikaCharsetFinder.java create mode 100644 src/main/java/org/alfresco/model/ContentModel.java create mode 100644 src/main/java/org/alfresco/model/RenditionModel.java create mode 100644 src/main/java/org/alfresco/opencmis/CMISAccessControlFormatEnum.java create mode 100644 src/main/java/org/alfresco/opencmis/CMISUtils.java create mode 100644 src/main/java/org/alfresco/opencmis/EnumFactory.java create mode 100644 src/main/java/org/alfresco/opencmis/EnumLabel.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/AbstractTypeDefinitionWrapper.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/BasePropertyDefintionWrapper.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/CMISAbstractDictionaryService.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/CMISActionEvaluator.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryRegistry.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryRegistryImpl.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryReload.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryService.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/CMISNodeInfo.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/CMISObjectVariant.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/CMISPropertyAccessor.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/CMISPropertyLuceneBuilder.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/CMISStrictDictionaryService.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/DocumentTypeDefinitionWrapper.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/FilteredDictionaryComponent.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/FolderTypeDefintionWrapper.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/ItemTypeDefinitionWrapper.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/PolicyTypeDefintionWrapper.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/PropertyAccessorMapping.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/PropertyDefinitionWrapper.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/PropertyLuceneBuilderMapping.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/QNameFilter.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/QNameFilterImpl.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/RelationshipTypeDefintionWrapper.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/SecondaryTypeDefinitionWrapper.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/ShadowTypeDefinitionWrapper.java create mode 100644 src/main/java/org/alfresco/opencmis/dictionary/TypeDefinitionWrapper.java create mode 100644 src/main/java/org/alfresco/opencmis/mapping/AbstractSimpleLuceneBuilder.java create mode 100644 src/main/java/org/alfresco/opencmis/mapping/BaseLuceneBuilder.java create mode 100644 src/main/java/org/alfresco/opencmis/mapping/BaseTypeIdLuceneBuilder.java create mode 100644 src/main/java/org/alfresco/opencmis/mapping/CMISMapping.java create mode 100644 src/main/java/org/alfresco/opencmis/mapping/ContentStreamLengthLuceneBuilder.java create mode 100644 src/main/java/org/alfresco/opencmis/mapping/ContentStreamMimetypeLuceneBuilder.java create mode 100644 src/main/java/org/alfresco/opencmis/mapping/DirectLuceneBuilder.java create mode 100644 src/main/java/org/alfresco/opencmis/mapping/FixedValueLuceneBuilder.java create mode 100644 src/main/java/org/alfresco/opencmis/mapping/NotSupportedLuceneBuilder.java create mode 100644 src/main/java/org/alfresco/opencmis/mapping/ObjectIdLuceneBuilder.java create mode 100644 src/main/java/org/alfresco/opencmis/mapping/ObjectTypeIdLuceneBuilder.java create mode 100644 src/main/java/org/alfresco/opencmis/mapping/ParentLuceneBuilder.java create mode 100644 src/main/java/org/alfresco/opencmis/mapping/RuntimePropertyLuceneBuilderMapping.java create mode 100644 src/main/java/org/alfresco/opencmis/search/CMISFTSQueryParser.java create mode 100644 src/main/java/org/alfresco/opencmis/search/CMISQueryOptions.java create mode 100644 src/main/java/org/alfresco/opencmis/search/CMISQueryParser.java create mode 100644 src/main/java/org/alfresco/opencmis/search/CmisFunctionEvaluationContext.java create mode 100644 src/main/java/org/alfresco/repo/cache/LockingCache.java create mode 100644 src/main/java/org/alfresco/repo/cache/MemoryCache.java create mode 100644 src/main/java/org/alfresco/repo/cache/SimpleCache.java create mode 100644 src/main/java/org/alfresco/repo/content/ContentContext.java create mode 100644 src/main/java/org/alfresco/repo/content/ContentStore.java create mode 100644 src/main/java/org/alfresco/repo/content/ContentStoreCaps.java create mode 100644 src/main/java/org/alfresco/repo/content/MimetypeMap.java create mode 100644 src/main/java/org/alfresco/repo/content/encoding/ContentCharsetFinder.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/AbstractDictionaryRegistry.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/CompiledModel.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/CompiledModelsCache.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/CoreDictionaryRegistryImpl.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/CustomModelDefinitionImpl.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/CustomModelsInfo.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/DelegateModelQuery.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/DictionaryBootstrap.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/DictionaryComponent.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/DictionaryDAO.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/DictionaryDAOImpl.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/DictionaryListener.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/DictionaryNamespaceComponent.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/DictionaryRegistry.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/ExtendedDictionaryListener.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/Facetable.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/IndexTokenisationMode.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2AnonymousTypeDefinition.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2Aspect.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2AspectDefinition.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2Association.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2AssociationDefinition.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2ChildAssociation.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2ChildAssociationDefinition.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2Class.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2ClassAssociation.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2ClassDefinition.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2Constraint.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2ConstraintDefinition.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2DataType.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2DataTypeDefinition.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2Label.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2Model.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2ModelDefinition.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2ModelDiff.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2NamedValue.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2Namespace.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2NamespaceDefinition.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2Property.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2PropertyDefinition.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2PropertyOverride.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2Type.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2TypeDefinition.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/M2XML.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/ModelQuery.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/NamespaceDAO.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/TenantDictionaryRegistryImpl.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/constraint/AbstractConstraint.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/constraint/ConstraintRegistry.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/constraint/ListOfValuesConstraint.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/constraint/NameChecker.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/constraint/NoOpConstraint.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/constraint/NumericRangeConstraint.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/constraint/RegexConstraint.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/constraint/RegisteredConstraint.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/constraint/StringLengthConstraint.java create mode 100644 src/main/java/org/alfresco/repo/dictionary/m2binding.xml create mode 100644 src/main/java/org/alfresco/repo/i18n/StaticMessageLookup.java create mode 100644 src/main/java/org/alfresco/repo/index/shard/Floc.java create mode 100644 src/main/java/org/alfresco/repo/index/shard/Shard.java create mode 100644 src/main/java/org/alfresco/repo/index/shard/ShardInstance.java create mode 100644 src/main/java/org/alfresco/repo/index/shard/ShardMethodEnum.java create mode 100644 src/main/java/org/alfresco/repo/index/shard/ShardState.java create mode 100644 src/main/java/org/alfresco/repo/index/shard/ShardStateBuilder.java create mode 100644 src/main/java/org/alfresco/repo/search/MLAnalysisMode.java create mode 100644 src/main/java/org/alfresco/repo/search/adaptor/lucene/AnalysisMode.java create mode 100644 src/main/java/org/alfresco/repo/search/adaptor/lucene/LuceneFunction.java create mode 100644 src/main/java/org/alfresco/repo/search/adaptor/lucene/LuceneQueryParserAdaptor.java create mode 100644 src/main/java/org/alfresco/repo/search/adaptor/lucene/LuceneQueryParserExpressionAdaptor.java create mode 100644 src/main/java/org/alfresco/repo/search/adaptor/lucene/QueryConstants.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/QueryParserUtils.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/AlfrescoFunctionEvaluationContext.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/CMIS.g create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/CMIS.tokens create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/CMISLexer.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/CMISParser.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/CMIS_FTS.g create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/CMIS_FTS.tokens create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/CMIS_FTSLexer.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/CMIS_FTSParser.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/FTS.g create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/FTS.tokens create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/FTSLexer.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/FTSParser.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/FTSQueryException.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/FTSQueryParser.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/parsers/GenerateUnicodeRanges.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/Argument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/ArgumentDefinition.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/Column.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/Conjunction.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/Constraint.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/Disjunction.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/DynamicArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/Function.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/FunctionArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/FunctionEvaluationContext.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/FunctionInvokation.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/FunctionalConstraint.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/Join.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/JoinType.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/ListArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/LiteralArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/Multiplicity.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/Order.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/Ordering.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/ParameterArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/PredicateMode.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/PropertyArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/Query.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/QueryEngine.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/QueryEngineResults.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/QueryModelException.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/QueryModelFactory.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/QueryOptions.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/Selector.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/SelectorArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/Source.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/StaticArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseArgumentDefinition.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseColumn.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseComparison.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseConjunction.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseConstraint.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseDisjunction.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseDynamicArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseFunction.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseFunctionArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseFunctionalConstraint.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseJoin.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseListArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseLiteralArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseOrdering.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseParameterArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BasePropertyArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseQuery.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseSelector.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseSelectorArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/BaseStaticArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/DuplicateSelectorNameException.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/SimpleConstraint.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/Child.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/Descendant.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/Equals.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/Exists.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/FTSFuzzyTerm.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/FTSPhrase.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/FTSPrefixTerm.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/FTSProximity.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/FTSRange.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/FTSTerm.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/FTSWildTerm.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/GreaterThan.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/GreaterThanOrEquals.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/In.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/LessThan.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/LessThanOrEquals.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/Like.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/Lower.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/NotEquals.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/PropertyAccessor.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/Score.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/functions/Upper.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneColumn.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneConjunction.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneDisjunction.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneFunctionArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneFunctionalConstraint.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneJoin.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneListArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneLiteralArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneOrdering.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneParameterArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LucenePropertyArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneQuery.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneQueryBuilder.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneQueryBuilderComponent.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneQueryBuilderContext.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneQueryModelFactory.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneSelector.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneSelectorArgument.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneChild.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneDescendant.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneEquals.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneExists.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneFTSFuzzyTerm.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneFTSPhrase.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneFTSPrefixTerm.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneFTSProximity.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneFTSRange.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneFTSTerm.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneFTSWildTerm.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneGreaterThan.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneGreaterThanOrEquals.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneIn.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneLessThan.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneLessThanOrEquals.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneLike.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneLower.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneNotEquals.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LucenePropertyAccessor.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneScore.java create mode 100644 src/main/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/functions/LuceneUpper.java create mode 100644 src/main/java/org/alfresco/repo/security/authentication/AlfrescoSecureContext.java create mode 100644 src/main/java/org/alfresco/repo/security/authentication/AlfrescoSecureContextImpl.java create mode 100644 src/main/java/org/alfresco/repo/security/authentication/AuthenticationDiagnostic.java create mode 100644 src/main/java/org/alfresco/repo/security/authentication/AuthenticationException.java create mode 100644 src/main/java/org/alfresco/repo/security/authentication/AuthenticationStep.java create mode 100644 src/main/java/org/alfresco/repo/security/authentication/AuthenticationUtil.java create mode 100644 src/main/java/org/alfresco/repo/security/authentication/InMemoryTicketComponentImpl.java create mode 100644 src/main/java/org/alfresco/repo/security/authentication/TicketComponent.java create mode 100644 src/main/java/org/alfresco/repo/security/authentication/TicketExpiredException.java create mode 100644 src/main/java/org/alfresco/repo/tenant/SingleTServiceImpl.java create mode 100644 src/main/java/org/alfresco/repo/tenant/Tenant.java create mode 100644 src/main/java/org/alfresco/repo/tenant/TenantContextHolder.java create mode 100644 src/main/java/org/alfresco/repo/tenant/TenantDeployer.java create mode 100644 src/main/java/org/alfresco/repo/tenant/TenantService.java create mode 100644 src/main/java/org/alfresco/repo/tenant/TenantUserService.java create mode 100644 src/main/java/org/alfresco/repo/tenant/TenantUtil.java create mode 100644 src/main/java/org/alfresco/service/Auditable.java create mode 100644 src/main/java/org/alfresco/service/NotAuditable.java create mode 100644 src/main/java/org/alfresco/service/PublicService.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/AspectDefinition.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/AssociationDefinition.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/ChildAssociationDefinition.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/ClassAttributeDefinition.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/ClassDefinition.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/Constraint.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/ConstraintDefinition.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/ConstraintException.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/CustomModelDefinition.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/CustomModelException.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/CustomModelService.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/DataTypeDefinition.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/DictionaryException.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/DictionaryService.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/InvalidAspectException.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/InvalidClassException.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/InvalidTypeException.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/ModelDefinition.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/NamespaceDefinition.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/PropertyDefinition.java create mode 100644 src/main/java/org/alfresco/service/cmr/dictionary/TypeDefinition.java create mode 100644 src/main/java/org/alfresco/service/cmr/i18n/MessageLookup.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/AbstractStoreException.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/AssociationExistsException.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/AssociationRef.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/ChildAssociationRef.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/ContentAccessor.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/ContentData.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/ContentIOException.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/ContentReader.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/ContentServiceTransientException.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/ContentStreamListener.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/ContentUrlKey.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/ContentWriter.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/EntityRef.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/FileContentReader.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/InvalidChildAssociationRefException.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/InvalidNodeRefException.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/InvalidStoreRefException.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/MLText.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/MalformedNodeRefException.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/MimetypeService.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/MimetypeServiceAware.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/NodeRef.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/NodeService.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/Path.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/Period.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/PeriodProvider.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/StoreExistsException.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/StoreRef.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/XPathException.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/datatype/DefaultTypeConverter.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/datatype/Duration.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/datatype/TypeConversionException.java create mode 100644 src/main/java/org/alfresco/service/cmr/repository/datatype/TypeConverter.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/BasicSearchParameters.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/LimitBy.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/NamedQueryParameterDefinition.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/PermissionEvaluationMode.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/QueryConsistency.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/QueryParameter.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/QueryParameterDefinition.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/ResultSet.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/ResultSetColumn.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/ResultSetMetaData.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/ResultSetRow.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/ResultSetSPI.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/ResultSetSelector.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/ResultSetType.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/SearchParameters.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/SearchService.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/SpellCheckResult.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/StatsParameters.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/StatsProcessor.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/StatsProcessorUsingMap.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/StatsResultSet.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/StatsResultStat.java create mode 100644 src/main/java/org/alfresco/service/cmr/search/StatsService.java create mode 100644 src/main/java/org/alfresco/service/cmr/security/AccessPermission.java create mode 100644 src/main/java/org/alfresco/service/cmr/security/AccessStatus.java create mode 100644 src/main/java/org/alfresco/service/cmr/security/AuthorityType.java create mode 100644 src/main/java/org/alfresco/service/cmr/security/PermissionContext.java create mode 100644 src/main/java/org/alfresco/service/cmr/security/PermissionService.java create mode 100644 src/main/java/org/alfresco/service/namespace/DynamicNamespacePrefixResolver.java create mode 100644 src/main/java/org/alfresco/service/namespace/InvalidQNameException.java create mode 100644 src/main/java/org/alfresco/service/namespace/NamespaceException.java create mode 100644 src/main/java/org/alfresco/service/namespace/NamespacePrefixResolver.java create mode 100644 src/main/java/org/alfresco/service/namespace/NamespacePrefixResolverProvider.java create mode 100644 src/main/java/org/alfresco/service/namespace/NamespaceService.java create mode 100644 src/main/java/org/alfresco/service/namespace/NamespaceServiceMemoryImpl.java create mode 100644 src/main/java/org/alfresco/service/namespace/QName.java create mode 100644 src/main/java/org/alfresco/service/namespace/QNameCache.java create mode 100644 src/main/java/org/alfresco/service/namespace/QNameMap.java create mode 100644 src/main/java/org/alfresco/service/namespace/QNamePattern.java create mode 100644 src/main/java/org/alfresco/service/namespace/RegexQNamePattern.java create mode 100644 src/main/java/org/alfresco/util/BaseApplicationContextHelper.java create mode 100644 src/main/java/org/alfresco/util/DataModelTestApplicationContextHelper.java create mode 100644 src/main/java/org/alfresco/util/ISO9075.java create mode 100644 src/main/java/org/alfresco/util/LazyClassPathXmlApplicationContext.java create mode 100644 src/main/java/org/alfresco/util/NoAutoStartClassPathXmlApplicationContext.java create mode 100644 src/main/java/org/alfresco/util/NumericEncoder.java create mode 100644 src/main/java/org/alfresco/util/SearchLanguageConversion.java create mode 100644 src/main/java/org/alfresco/util/WebApplicationContextLoader.java create mode 100644 src/main/java/org/apache/tika/mime/custom-mimetypes.xml create mode 100644 src/main/resources/alfresco/data-model-stand-alone-context.xml create mode 100644 src/main/resources/alfresco/messages/dictionary-messages.properties create mode 100644 src/main/resources/alfresco/messages/dictionary-model.properties create mode 100755 src/main/resources/alfresco/messages/dictionary-model_de.properties create mode 100755 src/main/resources/alfresco/messages/dictionary-model_es.properties create mode 100755 src/main/resources/alfresco/messages/dictionary-model_fr.properties create mode 100755 src/main/resources/alfresco/messages/dictionary-model_it.properties create mode 100755 src/main/resources/alfresco/messages/dictionary-model_ja.properties create mode 100755 src/main/resources/alfresco/messages/dictionary-model_nb.properties create mode 100755 src/main/resources/alfresco/messages/dictionary-model_nl.properties create mode 100644 src/main/resources/alfresco/messages/dictionary-model_pt_BR.properties create mode 100755 src/main/resources/alfresco/messages/dictionary-model_ru.properties create mode 100755 src/main/resources/alfresco/messages/dictionary-model_zh_CN.properties create mode 100644 src/main/resources/alfresco/mimetype/mimetype-map-openoffice.xml create mode 100644 src/main/resources/alfresco/mimetype/mimetype-map.xml create mode 100644 src/main/resources/alfresco/model/cmisModel.xml create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_cs.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_da.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_de.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_el.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_en.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_es.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_fi.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_fr.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_it.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_ja.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_ko.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_nl.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_no.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_pt.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_pt_BR.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_ru.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_sv.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_th.properties create mode 100644 src/main/resources/alfresco/model/dataTypeAnalyzers_zh.properties create mode 100644 src/main/resources/alfresco/model/dictionaryModel.xml create mode 100644 src/test/java/org/alfresco/opencmis/dictionary/CMISAbstractDictionaryServiceTest.java create mode 100644 src/test/java/org/alfresco/repo/cache/SimpleCacheTestBase.java create mode 100644 src/test/java/org/alfresco/repo/content/DataModelContentTestSuite.java create mode 100644 src/test/java/org/alfresco/repo/content/MimetypeMapTest.java create mode 100644 src/test/java/org/alfresco/repo/content/encoding/CharsetFinderTest.java create mode 100644 src/test/java/org/alfresco/repo/dictionary/AbstractModelTest.java create mode 100644 src/test/java/org/alfresco/repo/dictionary/DictionaryComponentTest.java create mode 100644 src/test/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java create mode 100644 src/test/java/org/alfresco/repo/dictionary/DiffModelTest.java create mode 100644 src/test/java/org/alfresco/repo/dictionary/constraint/ConstraintsTest.java create mode 100644 src/test/java/org/alfresco/repo/index/ShardMethodEnumTest.java create mode 100644 src/test/java/org/alfresco/repo/search/impl/parsers/CMISTest.java create mode 100644 src/test/java/org/alfresco/repo/search/impl/parsers/CMIS_FTSTest.java create mode 100644 src/test/java/org/alfresco/repo/search/impl/parsers/FTSTest.java create mode 100644 src/test/java/org/alfresco/repo/search/impl/parsers/gUnitExecutor.java create mode 100644 src/test/java/org/alfresco/service/cmr/repository/MLTextTest.java create mode 100644 src/test/java/org/alfresco/service/cmr/repository/NodeRefTest.java create mode 100644 src/test/java/org/alfresco/service/cmr/repository/PathTest.java create mode 100644 src/test/java/org/alfresco/service/cmr/repository/datatype/DefaultTypeConverterTest.java create mode 100644 src/test/java/org/alfresco/service/cmr/search/StatsProcessorTest.java create mode 100644 src/test/java/org/alfresco/service/namespace/DynamicNameSpaceResolverTest.java create mode 100644 src/test/java/org/alfresco/service/namespace/QNamePatternTest.java create mode 100644 src/test/java/org/alfresco/service/namespace/QNameTest.java create mode 100644 src/test/java/org/alfresco/util/ISO9075Test.java create mode 100644 src/test/java/org/alfresco/util/NumericEncodingTest.java create mode 100644 src/test/java/org/alfresco/util/SearchLanguageConversionTest.java create mode 100644 src/test/resources/org/alfresco/repo/dictionary/commonpropertynschild_model.xml create mode 100644 src/test/resources/org/alfresco/repo/dictionary/commonpropertynsparent_model.xml create mode 100644 src/test/resources/org/alfresco/repo/dictionary/dictionarydaotest_model.properties create mode 100644 src/test/resources/org/alfresco/repo/dictionary/dictionarydaotest_model.xml create mode 100644 src/test/resources/org/alfresco/repo/dictionary/dictionarydaotest_model1.properties create mode 100644 src/test/resources/org/alfresco/repo/dictionary/dictionarydaotest_model1.xml create mode 100644 src/test/resources/org/alfresco/repo/dictionary/modelCircularTypes.xml create mode 100644 src/test/resources/org/alfresco/repo/dictionary/nstest_model.xml create mode 100644 src/test/resources/org/alfresco/repo/search/impl/parsers/cmis_fts_test.gunit create mode 100644 src/test/resources/org/alfresco/repo/search/impl/parsers/cmis_test.gunit create mode 100644 src/test/resources/org/alfresco/repo/search/impl/parsers/fts_test.gunit diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f985ec4 --- /dev/null +++ b/pom.xml @@ -0,0 +1,252 @@ + + 4.0.0 + alfresco-data-model + Alfresco Data model classes + + alfresco-parent + org.alfresco + 5.2.1-SNAPSHOT + ../../pom.xml + + + + + + source/java/org/alfresco/repo/search/impl/parsers/CMIS*er.java, + source/java/org/alfresco/repo/search/impl/parsers/FTSParser.java, + source/java/org/alfresco/repo/search/impl/parsers/FTSLexer.java + + + + + + + + org.jibx + maven-jibx-plugin + + true + ${project.build.sourceDirectory}/org/alfresco/repo/dictionary + + m2binding.xml + + + + + bind-sources + + bind + + + + + + + + maven-antrun-plugin + + + duplicate-english-messages + generate-resources + + run + + + + + + + + + + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.jibx + + maven-jibx-plugin + + + [1.2.5,) + + + bind + + + + + true + true + + + + + + + + + + + + + + org.alfresco + alfresco-core + + + + javax.servlet + servlet-api + provided + + + + jaxen + jaxen + + + org.jibx + jibx-run + ${dependency.jibx.version} + + + + org.antlr + antlr + ${dependency.antlr.version} + + + org.apache.chemistry.opencmis + chemistry-opencmis-client-impl + ${dependency.opencmis.version} + + + junit + junit + + + org.jvnet.staxex + stax-ex + + + + + org.apache.chemistry.opencmis + chemistry-opencmis-commons-impl + ${dependency.opencmis.version} + + + com.sun.xml.messaging.saaj + saaj-impl + + + org.jvnet.staxex + stax-ex + + + + + + + org.jvnet.staxex + stax-ex + 1.2 + + + org.apache.chemistry.opencmis + chemistry-opencmis-server-bindings + ${dependency.opencmis.version} + + + org.jvnet.staxex + stax-ex + + + + + org.codehaus.woodstox + woodstox-core-asl + 4.2.0 + + + + org.acegisecurity + acegi-security + 0.8.2_patched + + + org.alfresco + alfresco-xmlfactory + + + xpp3 + xpp3 + 1.1.3_8 + + + + org.apache.tika + tika-core + ${dependency.tika.version} + + + org.apache.tika + tika-parsers + ${dependency.tika.version} + + + org.gagravarr + vorbis-java-core + ${dependency.vorbisJava.version} + + + org.gagravarr + vorbis-java-tika + ${dependency.vorbisJava.version} + + + com.googlecode.juniversalchardet + juniversalchardet + 1.0.3 + + + + + junit + junit + test + + + org.antlr + gunit + ${dependency.antlr.version} + test + + + org.mockito + mockito-all + test + + + diff --git a/src/main/java/org/alfresco/encoding/TikaCharsetFinder.java b/src/main/java/org/alfresco/encoding/TikaCharsetFinder.java new file mode 100644 index 0000000..2c7b98c --- /dev/null +++ b/src/main/java/org/alfresco/encoding/TikaCharsetFinder.java @@ -0,0 +1,95 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.encoding; + +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.tika.parser.txt.CharsetDetector; +import org.apache.tika.parser.txt.CharsetMatch; + +/** + * Uses Apache Tika as a fallback encoding detector + * + * @since 3.4 + * @author Nick Burch + */ +public class TikaCharsetFinder extends AbstractCharactersetFinder +{ + private static Log logger = LogFactory.getLog(TikaCharsetFinder.class); + + private int threshold = 35; + + @Override + protected Charset detectCharsetImpl(byte[] buffer) throws Exception + { + CharsetDetector detector = new CharsetDetector(); + detector.setText(buffer); + CharsetMatch match = detector.detect(); + + if(match != null && match.getConfidence() > threshold) + { + try + { + return Charset.forName(match.getName()); + } + catch(UnsupportedCharsetException e) + { + logger.info("Charset detected as " + match.getName() + " but the JVM does not support this, detection skipped"); + } + } + return null; + } + + /** + * Return the matching threshold before we decide that + * what we detected is a good match. In the range + * 0-100. + */ + public int getThreshold() + { + return threshold; + } + + /** + * At what point do we decide our match is good enough? + * In the range 0-100. If we don't reach the threshold, + * we'll decline, and either another finder will work on + * it or the fallback encoding will be taken. + */ + public void setThreshold(int threshold) + { + if(threshold < 0) + threshold = 0; + if(threshold > 100) + threshold = 100; + + this.threshold = threshold; + } + +} diff --git a/src/main/java/org/alfresco/model/ContentModel.java b/src/main/java/org/alfresco/model/ContentModel.java new file mode 100644 index 0000000..bfac3dc --- /dev/null +++ b/src/main/java/org/alfresco/model/ContentModel.java @@ -0,0 +1,470 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.model; + +import org.alfresco.api.AlfrescoPublicApi; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + + +/** + * Content Model Constants + */ +@AlfrescoPublicApi +public interface ContentModel +{ + // + // System Model Definitions + // + + // type for deleted nodes + static final QName TYPE_DELETED = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "deleted"); + static final QName PROP_ORIGINAL_ID = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "originalId"); + + // base type constants + static final QName TYPE_BASE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "base"); + static final QName ASPECT_REFERENCEABLE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "referenceable"); + static final QName PROP_STORE_PROTOCOL = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "store-protocol"); + static final QName PROP_STORE_IDENTIFIER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "store-identifier"); + static final QName PROP_NODE_UUID = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-uuid"); + static final QName PROP_NODE_DBID = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-dbid"); + + // tag for incomplete nodes + static final QName ASPECT_INCOMPLETE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "incomplete"); + + // tag for temporary nodes + static final QName ASPECT_TEMPORARY = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "temporary"); + + // tag for nodes being formed (CIFS) + static final QName ASPECT_NO_CONTENT = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "noContent"); + + // tag for nodes being formed (WebDAV) + static final QName ASPECT_WEBDAV_NO_CONTENT = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "webdavNoContent"); + + static final QName ASPECT_WEBDAV_OBJECT = QName.createQName(NamespaceService.WEBDAV_MODEL_1_0_URI, "object"); + static final QName PROP_DEAD_PROPERTIES = QName.createQName(NamespaceService.WEBDAV_MODEL_1_0_URI, "deadproperties"); + + // tag for localized nodes + static final QName ASPECT_LOCALIZED = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "localized"); + static final QName PROP_LOCALE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "locale"); + + // tag for hidden nodes + static final QName ASPECT_HIDDEN = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "hidden"); + static final QName PROP_VISIBILITY_MASK = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "clientVisibilityMask"); + static final QName PROP_HIDDEN_FLAG = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "hiddenFlag"); + static final QName PROP_CASCADE_HIDDEN = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "cascadeHidden"); + static final QName PROP_CASCADE_INDEX_CONTROL = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "cascadeIndexControl"); + static final QName PROP_CLIENT_CONTROLLED = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "clientControlled"); + + // tag for soft delete (CIFS rename shuffle) + static final QName ASPECT_SOFT_DELETE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "softDelete"); + + // archived nodes aspect constants + static final QName ASPECT_ARCHIVE_ROOT = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archiveRoot"); + static final QName ASSOC_ARCHIVE_USER_LINK = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archiveUserLink"); + static final QName TYPE_ARCHIVE_USER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archiveUser"); + static final QName ASSOC_ARCHIVED_LINK = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedLink"); + static final QName ASPECT_ARCHIVED = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archived"); + static final QName PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedOriginalParentAssoc"); + static final QName PROP_ARCHIVED_BY = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedBy"); + static final QName PROP_ARCHIVED_DATE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedDate"); + static final QName PROP_ARCHIVED_ORIGINAL_OWNER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedOriginalOwner"); + static final QName ASPECT_ARCHIVED_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archived-assocs"); + static final QName PROP_ARCHIVED_PARENT_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedParentAssocs"); + static final QName PROP_ARCHIVED_CHILD_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedChildAssocs"); + static final QName PROP_ARCHIVED_SOURCE_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedSourceAssocs"); + static final QName PROP_ARCHIVED_TARGET_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedTargetAssocs"); + static final QName ASPECT_ARCHIVE_LOCKABLE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedLockable"); + static final QName PROP_ARCHIVED_LOCK_OWNER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedLockOwner"); + static final QName PROP_ARCHIVED_LOCK_TYPE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedLockType"); + static final QName PROP_ARCHIVED_LOCK_LIFETIME = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedLockLifetime"); + static final QName PROP_ARCHIVED_EXPIRY_DATE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedExpiryDate"); + static final QName PROP_ARCHIVED_LOCK_ADDITIONAL_INFO = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedLockAdditionalInfo"); + + + // referenceable aspect constants + static final QName TYPE_REFERENCE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "reference"); + static final QName PROP_REFERENCE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "reference"); + + // container type constants + static final QName TYPE_CONTAINER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "container"); + /** child association type supported by {@link #TYPE_CONTAINER} */ + static final QName ASSOC_CHILDREN =QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "children"); + + // roots + static final QName ASPECT_ROOT = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "aspect_root"); + static final QName TYPE_STOREROOT = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "store_root"); + + // for internal use only: see ALF-13066 / ALF-12358 + static final QName TYPE_LOST_AND_FOUND = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "lost_found"); + static final QName ASSOC_LOST_AND_FOUND = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "lost_found"); + + // descriptor properties + static final QName PROP_SYS_NAME = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "name"); + static final QName PROP_SYS_VERSION_MAJOR = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionMajor"); + static final QName PROP_SYS_VERSION_MINOR = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionMinor"); + static final QName PROP_SYS_VERSION_REVISION = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionRevision"); + static final QName PROP_SYS_VERSION_LABEL = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionLabel"); + static final QName PROP_SYS_VERSION_BUILD = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionBuild"); + static final QName PROP_SYS_VERSION_SCHEMA = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionSchema"); + static final QName PROP_SYS_VERSION_EDITION = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionEdition"); + static final QName PROP_SYS_VERSION_PROPERTIES = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionProperties"); + static final QName PROP_SYS_LICENSE_MODE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "licenseMode"); + + /** + * Aspect for nodes which are by default not deletable. + * @since 3.5.0 + */ + static final QName ASPECT_UNDELETABLE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "undeletable"); + + /** + * Aspect for nodes which are by default not movable. + */ + static final QName ASPECT_UNMOVABLE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "unmovable"); + + /** + * Aspects marking nodes that are pending deletion. + * This aspect is applied to all nodes that are about to be deleted within a transaction. + * The aspect survives only for the duration of calls to delete nodes and their children. + */ + static final QName ASPECT_PENDING_DELETE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "pendingDelete"); + + /** + * Aspect marking nodes for pending set fixed ACL operation and it's contants + */ + static final QName ASPECT_PENDING_FIX_ACL = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "pendingFixAcl"); + static final QName PROP_SHARED_ACL_TO_REPLACE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "sharedAclToReplace"); + static final QName PROP_INHERIT_FROM_ACL = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "inheritFromAcl"); + + // + // Content Model Definitions + // + + // content management type constants + static final QName TYPE_CMOBJECT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "cmobject"); + static final QName PROP_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "name"); + + // copy aspect constants + static final QName ASPECT_COPIEDFROM = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "copiedfrom"); + static final QName ASSOC_ORIGINAL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "original"); + + // working copy aspect contants + static final QName ASPECT_CHECKED_OUT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "checkedOut"); + static final QName ASSOC_WORKING_COPY_LINK = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "workingcopylink"); + static final QName ASPECT_WORKING_COPY = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "workingcopy"); + static final QName PROP_WORKING_COPY_OWNER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "workingCopyOwner"); + static final QName PROP_WORKING_COPY_MODE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "workingCopyMode"); + static final QName PROP_WORKING_COPY_LABEL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "workingCopyLabel"); + + // content type and aspect constants + static final QName TYPE_CONTENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "content"); + static final QName PROP_CONTENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "content"); + + // title aspect + static final QName ASPECT_TITLED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "titled"); + static final QName PROP_TITLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "title"); + static final QName PROP_DESCRIPTION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "description"); + + // auditable aspect + static final QName ASPECT_AUDITABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "auditable"); + static final QName PROP_CREATED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "created"); + static final QName PROP_CREATOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "creator"); + static final QName PROP_MODIFIED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modified"); + static final QName PROP_MODIFIER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modifier"); + static final QName PROP_ACCESSED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "accessed"); + + // author aspect + static final QName ASPECT_AUTHOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "author"); + static final QName PROP_AUTHOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "author"); + + // categories + static final QName TYPE_CATEGORYROOT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "category_root"); + static final QName ASPECT_CLASSIFIABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "classifiable"); + //static final QName ASPECT_CATEGORISATION = QName.createQName(NamespaceService.ALFRESCO_URI, "aspect_categorisation"); + static final QName ASPECT_GEN_CLASSIFIABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "generalclassifiable"); + static final QName TYPE_CATEGORY = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "category"); + static final QName PROP_CATEGORIES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "categories"); + static final QName ASSOC_CATEGORIES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "categories"); + static final QName ASSOC_SUBCATEGORIES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "subcategories"); + + // tags - a subsection of categories + static final QName ASPECT_TAGGABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "taggable"); + static final QName PROP_TAGS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "taggable"); + + // tagscope aspect + static final QName ASPECT_TAGSCOPE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "tagscope"); + static final QName PROP_TAGSCOPE_CACHE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "tagScopeCache"); + static final QName PROP_TAGSCOPE_SUMMARY = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "tagScopeSummary"); + + // ratings + static final QName ASPECT_RATEABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "rateable"); + static final QName ASPECT_LIKES_RATING_SCHEME_ROLLUPS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "likesRatingSchemeRollups"); + static final QName ASPECT_FIVESTAR_RATING_SCHEME_ROLLUPS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "fiveStarRatingSchemeRollups"); + static final QName ASSOC_RATINGS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "ratings"); + static final QName TYPE_RATING = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "rating"); + static final QName PROP_RATING_SCORE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "ratingScore"); + static final QName PROP_RATING_SCHEME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "ratingScheme"); + static final QName PROP_RATED_AT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "ratedAt"); + + // lock aspect + public final static QName ASPECT_LOCKABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockable"); + public final static QName PROP_LOCK_OWNER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockOwner"); + public final static QName PROP_LOCK_TYPE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockType"); + public final static QName PROP_LOCK_LIFETIME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockLifetime"); + public final static QName PROP_EXPIRY_DATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "expiryDate"); + public final static QName PROP_LOCK_ADDITIONAL_INFO = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockAdditionalInfo"); + + // version aspect + static final QName ASPECT_VERSIONABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "versionable"); + static final QName PROP_VERSION_LABEL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "versionLabel"); + static final QName PROP_INITIAL_VERSION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "initialVersion"); + static final QName PROP_AUTO_VERSION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "autoVersion"); + static final QName PROP_AUTO_VERSION_PROPS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "autoVersionOnUpdateProps"); + static final QName PROP_VERSION_TYPE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "versionType"); + + // folders + static final QName TYPE_SYSTEM_FOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "systemfolder"); + static final QName TYPE_FOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "folder"); + /** child association type supported by {@link #TYPE_FOLDER} */ + static final QName ASSOC_CONTAINS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "contains"); + + // person + static final QName TYPE_PERSON = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "person"); + static final QName PROP_USERNAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "userName"); + static final QName PROP_HOMEFOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "homeFolder"); + static final QName PROP_FIRSTNAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "firstName"); + static final QName PROP_LASTNAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lastName"); + static final QName PROP_EMAIL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "email"); + static final QName PROP_ORGID = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "organizationId"); + static final QName PROP_HOME_FOLDER_PROVIDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "homeFolderProvider"); + static final QName PROP_DEFAULT_HOME_FOLDER_PATH = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "defaultHomeFolderPath"); + static final QName PROP_PRESENCEPROVIDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "presenceProvider"); + static final QName PROP_PRESENCEUSERNAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "presenceUsername"); + static final QName PROP_ORGANIZATION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "organization"); + static final QName PROP_JOBTITLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "jobtitle"); + static final QName PROP_LOCATION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "location"); + static final QName PROP_PERSONDESC = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "persondescription"); + static final QName PROP_TELEPHONE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "telephone"); + static final QName PROP_MOBILE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mobile"); + static final QName PROP_COMPANYADDRESS1 = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "companyaddress1"); + static final QName PROP_COMPANYADDRESS2 = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "companyaddress2"); + static final QName PROP_COMPANYADDRESS3 = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "companyaddress3"); + static final QName PROP_COMPANYPOSTCODE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "companypostcode"); + static final QName PROP_COMPANYTELEPHONE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "companytelephone"); + static final QName PROP_COMPANYFAX = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "companyfax"); + static final QName PROP_COMPANYEMAIL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "companyemail"); + static final QName PROP_SKYPE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "skype"); + static final QName PROP_GOOGLEUSERNAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "googleusername"); + static final QName PROP_INSTANTMSG = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "instantmsg"); + static final QName PROP_USER_STATUS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "userStatus"); + static final QName PROP_USER_STATUS_TIME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "userStatusTime"); + + static final QName PROP_SIZE_CURRENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "sizeCurrent"); // system-maintained + static final QName PROP_SIZE_QUOTA = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "sizeQuota"); + + static final QName PROP_EMAIL_FEED_ID = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "emailFeedId"); // system-maintained + static final QName PROP_EMAIL_FEED_DISABLED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "emailFeedDisabled"); + + static final QName PROP_SUBSCRIPTIONS_PRIVATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "subscriptionsPrivate"); + + static final QName ASPECT_PERSON_DISABLED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "personDisabled"); + + static final QName ASPECT_ANULLABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "annullable"); + + static final QName ASSOC_AVATAR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avatar"); + + // Authority + static final QName TYPE_AUTHORITY = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "authority"); + + static final QName TYPE_AUTHORITY_CONTAINER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "authorityContainer"); + static final QName PROP_AUTHORITY_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "authorityName"); + static final QName PROP_AUTHORITY_DISPLAY_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "authorityDisplayName"); + + static final QName ASSOC_MEMBER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "member"); + + // Zone + static final QName TYPE_ZONE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "zone"); + static final QName ASSOC_IN_ZONE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "inZone"); + + // Ownable aspect + static final QName ASPECT_OWNABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "ownable"); + static final QName PROP_OWNER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "owner"); + + // Templatable aspect + static final QName ASPECT_TEMPLATABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "templatable"); + static final QName PROP_TEMPLATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "template"); + + // Webscriptable aspect + static final QName ASPECT_WEBSCRIPTABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "webscriptable"); + static final QName PROP_WEBSCRIPT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "webscript"); + + // Dictionary model + static final QName TYPE_DICTIONARY_MODEL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "dictionaryModel"); + static final QName PROP_MODEL_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelName"); + static final QName PROP_MODEL_DESCRIPTION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelDescription"); + static final QName PROP_MODEL_AUTHOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelAuthor"); + static final QName PROP_MODEL_PUBLISHED_DATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelPublishedDate"); + static final QName PROP_MODEL_VERSION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelVersion"); + static final QName PROP_MODEL_ACTIVE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelActive"); + + // referencing aspect + static final QName ASPECT_REFERENCING = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "referencing"); + static final QName ASSOC_REFERENCES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "references"); + + // link object + static final QName TYPE_LINK = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "link"); + static final QName PROP_LINK_DESTINATION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "destination"); + + // attachable aspect + static final QName ASPECT_ATTACHABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "attachable"); + static final QName ASSOC_ATTACHMENTS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "attachments"); + + // emailed aspect + static final QName ASPECT_EMAILED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "emailed"); + static final QName PROP_SENTDATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "sentdate"); + static final QName PROP_ORIGINATOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "originator"); + static final QName PROP_ADDRESSEE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "addressee"); + static final QName PROP_ADDRESSEES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "addressees"); + static final QName PROP_SUBJECT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "subjectline"); + + // countable aspect + static final QName ASPECT_COUNTABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "countable"); + static final QName PROP_HITS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "hits"); + static final QName PROP_COUNTER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "counter"); + + // References Node Aspect. + static final QName ASPECT_REFERENCES_NODE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "referencesnode"); + static final QName PROP_NODE_REF = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "noderef"); + + // Geographic Aspect. + static final QName ASPECT_GEOGRAPHIC = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "geographic"); + static final QName PROP_LATITUDE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "latitude"); + static final QName PROP_LONGITUDE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "longitude"); + + // Multilingual Type + static final QName TYPE_MULTILINGUAL_CONTAINER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlContainer"); + static final QName ASSOC_MULTILINGUAL_CHILD = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlChild"); + static final QName ASPECT_MULTILINGUAL_DOCUMENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlDocument"); + static final QName ASPECT_MULTILINGUAL_EMPTY_TRANSLATION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlEmptyTranslation"); + + // Thumbnail Type + static final QName TYPE_THUMBNAIL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "thumbnail"); + static final QName PROP_THUMBNAIL_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "thumbnailName"); + static final QName PROP_CONTENT_PROPERTY_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "contentPropertyName"); + static final QName PROP_AUTOMATIC_UPDATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "automaticUpdate"); + + // Thumbnail modification handling + public static final QName ASPECT_THUMBNAIL_MODIFICATION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "thumbnailModification"); + public static final QName PROP_LAST_THUMBNAIL_MODIFICATION_DATA = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lastThumbnailModification"); + + // The below content entities can be used to manage 'failed' thumbnails. These are thumbnails that execute and fail with an + // exception that likely means a reattempt will fail. The failedThumbnailSource aspect can be used to mark a node as + // having tried and failed to use a particular thumbnail definition. This can then be checked and reattempts at that thumbnail + // can be prevented or throttled. + static final QName ASPECT_FAILED_THUMBNAIL_SOURCE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "failedThumbnailSource"); + static final QName ASSOC_FAILED_THUMBNAIL= QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "failedThumbnail"); + static final QName TYPE_FAILED_THUMBNAIL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "failedThumbnail"); + static final QName PROP_FAILED_THUMBNAIL_TIME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "failedThumbnailTime"); + static final QName PROP_FAILURE_COUNT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "failureCount"); + + // Thumbnailed Aspect + /** + * This aspect type has been deprecated. + * From Alfresco 3.3 the {@link RenditionModel#ASPECT_RENDITIONED rn:renditioned} + * (which is a child of cm:thumbnailed) should be used instead. + */ + @Deprecated + static final QName ASPECT_THUMBNAILED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "thumbnailed"); + /** + * This association type has been deprecated. + * From Alfresco 3.3 this association no longer exists and has been replaced with + * {@link RenditionModel#ASSOC_RENDITION rn:rendition} association. From Alfresco + * 3.3 onwards a patch is executed at startup which renames + * the cm:thumbnails QName to rn:rendition in the database. + *

+ * This field has been updated to point to that association and references to this + * field should be updated to use the new field. + */ + @Deprecated + static final QName ASSOC_THUMBNAILS = RenditionModel.ASSOC_RENDITION; + + // StoreSelector Aspect + static final QName ASPECT_STORE_SELECTOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "storeSelector"); + static final QName PROP_STORE_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "storeName"); + + // Preference Aspect + static final QName ASPECT_PREFERENCES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "preferences"); + static final QName PROP_PREFERENCE_VALUES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "preferenceValues"); + static final QName ASSOC_PREFERENCE_IMAGE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "preferenceImage"); + + // Syndication Aspect + static final QName ASPECT_SYNDICATION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "syndication"); + static final QName PROP_PUBLISHED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "published"); + static final QName PROP_UPDATED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "updated"); + + // Dublin core aspect + static final QName ASPECT_DUBLINCORE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "dublincore"); + + // + // User Model Definitions + // + + static final String USER_MODEL_URI = "http://www.alfresco.org/model/user/1.0"; + static final String USER_MODEL_PREFIX = "usr"; + + static final QName TYPE_USER = QName.createQName(USER_MODEL_URI, "user"); + static final QName PROP_USER_USERNAME = QName.createQName(USER_MODEL_URI, "username"); + static final QName PROP_PASSWORD = QName.createQName(USER_MODEL_URI, "password"); + static final QName PROP_PASSWORD_SHA256 = QName.createQName(USER_MODEL_URI, "password2"); + static final QName PROP_PASSWORD_HASH = QName.createQName(USER_MODEL_URI, "passwordHash"); + static final QName PROP_ENABLED = QName.createQName(USER_MODEL_URI, "enabled"); + static final QName PROP_ACCOUNT_EXPIRES = QName.createQName(USER_MODEL_URI, "accountExpires"); + static final QName PROP_ACCOUNT_EXPIRY_DATE = QName.createQName(USER_MODEL_URI, "accountExpiryDate"); + static final QName PROP_CREDENTIALS_EXPIRE = QName.createQName(USER_MODEL_URI, "credentialsExpire"); + static final QName PROP_CREDENTIALS_EXPIRY_DATE = QName.createQName(USER_MODEL_URI, "credentialsExpiryDate"); + static final QName PROP_ACCOUNT_LOCKED = QName.createQName(USER_MODEL_URI, "accountLocked"); + static final QName PROP_SALT = QName.createQName(USER_MODEL_URI, "salt"); + static final QName PROP_HASH_INDICATOR = QName.createQName(USER_MODEL_URI, "hashIndicator"); + + // + // Indexing control + // + + static final QName ASPECT_INDEX_CONTROL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "indexControl"); + static final QName PROP_IS_INDEXED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "isIndexed"); + static final QName PROP_IS_CONTENT_INDEXED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "isContentIndexed"); + + // CMIS aspects + static final QName ASPECT_CMIS_UPDATE_CONTEXT = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "CMISUpdateContext"); + static final QName PROP_GOT_FIRST_CHUNK = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "gotFirstChunk"); + static final QName ASPECT_CMIS_CREATED_CHECKEDOUT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "cmisCreatedCheckedOut"); + + // Cascade Update + static final QName ASPECT_CASCADE_UPDATE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "cascadeUpdate"); + static final QName PROP_CASCADE_CRC = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "cascadeCRC"); + static final QName PROP_CASCADE_TX = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "cascadeTx"); + +} diff --git a/src/main/java/org/alfresco/model/RenditionModel.java b/src/main/java/org/alfresco/model/RenditionModel.java new file mode 100644 index 0000000..c7d41d7 --- /dev/null +++ b/src/main/java/org/alfresco/model/RenditionModel.java @@ -0,0 +1,49 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.model; + +import org.alfresco.api.AlfrescoPublicApi; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Rendition Model Constants + */ +@AlfrescoPublicApi +public interface RenditionModel +{ + static final QName ASPECT_RENDITION = QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "rendition"); + static final QName ASPECT_HIDDEN_RENDITION = QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "hiddenRendition"); + static final QName ASPECT_VISIBLE_RENDITION = QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "visibleRendition"); + + static final QName ASPECT_RENDITIONED = QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "renditioned"); + static final QName ASSOC_RENDITION = QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "rendition"); + + /** + * @since 4.0.1 + */ + static final QName ASPECT_PREVENT_RENDITIONS = QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "preventRenditions"); +} diff --git a/src/main/java/org/alfresco/opencmis/CMISAccessControlFormatEnum.java b/src/main/java/org/alfresco/opencmis/CMISAccessControlFormatEnum.java new file mode 100644 index 0000000..e4244ae --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/CMISAccessControlFormatEnum.java @@ -0,0 +1,71 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis; + +/** + * @author andyh + * + */ +public enum CMISAccessControlFormatEnum implements EnumLabel +{ + /** + * Report only CMIS basic permissions + */ + CMIS_BASIC_PERMISSIONS("onlyBasicPermissions"), + + /** + * May report CMIS basic permission, repository specific permissions or a mixture of both. + */ + REPOSITORY_SPECIFIC_PERMISSIONS("repositorySpecificPermissions"); + + private String label; + + /** + * Construct + * + * @param label String + */ + CMISAccessControlFormatEnum(String label) + { + this.label = label; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.cmis.EnumLabel#label() + */ + public String getLabel() + { + return label; + } + + /** + * Factory for CMISAclPropagationEnum + */ + public static EnumFactory FACTORY = new EnumFactory(CMISAccessControlFormatEnum.class, CMIS_BASIC_PERMISSIONS, true); + +} diff --git a/src/main/java/org/alfresco/opencmis/CMISUtils.java b/src/main/java/org/alfresco/opencmis/CMISUtils.java new file mode 100644 index 0000000..29f5a5d --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/CMISUtils.java @@ -0,0 +1,169 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis; + +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; + +import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException; + +public class CMISUtils +{ + @SuppressWarnings("unchecked") + public static T copy(T source) + { + T target = null; + try + { + CopyOutputStream cos = new CopyOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(cos); + out.writeObject(source); + out.flush(); + out.close(); + + ObjectInputStream in = new ObjectInputStream(cos.getInputStream()); + target = (T) in.readObject(); + } catch (Exception e) + { + throw new CmisRuntimeException("Object copy failed!", e); + } + + return target; + } + + private static class CopyOutputStream extends OutputStream + { + protected byte[] buf = null; + protected int size = 0; + + public CopyOutputStream() + { + this(16 * 1024); + } + + public CopyOutputStream(int initSize) + { + this.size = 0; + this.buf = new byte[initSize]; + } + + private void verifyBufferSize(int sz) + { + if (sz > buf.length) + { + byte[] old = buf; + buf = new byte[Math.max(sz, 2 * buf.length)]; + System.arraycopy(old, 0, buf, 0, old.length); + old = null; + } + } + + public final void write(byte b[]) + { + verifyBufferSize(size + b.length); + System.arraycopy(b, 0, buf, size, b.length); + size += b.length; + } + + public final void write(byte b[], int off, int len) + { + verifyBufferSize(size + len); + System.arraycopy(b, off, buf, size, len); + size += len; + } + + public final void write(int b) + { + verifyBufferSize(size + 1); + buf[size++] = (byte) b; + } + + public InputStream getInputStream() + { + return new CopyInputStream(buf, size); + } + } + + private static class CopyInputStream extends InputStream + { + protected byte[] buf = null; + protected int count = 0; + protected int pos = 0; + + public CopyInputStream(byte[] buf, int count) + { + this.buf = buf; + this.count = count; + } + + public final int available() + { + return count - pos; + } + + public final int read() + { + return (pos < count) ? (buf[pos++] & 0xff) : -1; + } + + public final int read(byte[] b, int off, int len) + { + if (pos >= count) + { + return -1; + } + + if ((pos + len) > count) + { + len = (count - pos); + } + + System.arraycopy(buf, pos, b, off, len); + pos += len; + + return len; + } + + public final long skip(long n) + { + if ((pos + n) > count) + { + n = count - pos; + } + + if (n < 0) + { + return 0; + } + + pos += n; + + return n; + } + } +} diff --git a/src/main/java/org/alfresco/opencmis/EnumFactory.java b/src/main/java/org/alfresco/opencmis/EnumFactory.java new file mode 100644 index 0000000..7fe60b4 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/EnumFactory.java @@ -0,0 +1,151 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeMap; + + +/** + * Enum Factory for managing mapping between Enum name and Enum label + * + * @author dcaruana + * + */ +public class EnumFactory> +{ + private E defaultEnum; + private Map labelMap = new HashMap(10); + + public EnumFactory(Class enumClass) + { + this(enumClass, null, false); + } + + public EnumFactory(Class enumClass, E defaultEnum) + { + this(enumClass, defaultEnum, false); + } + + /** + * @param caseSensitive case-sensitive lookup for Enum label + */ + public EnumFactory(Class enumClass, E defaultEnum, boolean caseSensitive) + { + this.defaultEnum = defaultEnum; + + // setup label map + labelMap = caseSensitive ? new HashMap(10) : new TreeMap(String.CASE_INSENSITIVE_ORDER); + EnumSet enumSet = EnumSet.allOf(enumClass); + Iterator iter = enumSet.iterator(); + while(iter.hasNext()) + { + E e = iter.next(); + if (e instanceof EnumLabel) + { + labelMap.put(((EnumLabel)e).getLabel(), e); + } + } + } + + /** + * Gets the default enum + * + * @return default enum (or null, if no default specified) + */ + public Enum getDefaultEnum() + { + return defaultEnum; + } + + /** + * Gets the default label + * + * @return label of default enum (or null, if no default specified) + */ + public String getDefaultLabel() + { + return label(defaultEnum); + } + + /** + * Gets the label for the specified enum + * + * @param e enum + * @return label (or null, if no label specified) + */ + public String label(E e) + { + if (e instanceof EnumLabel) + { + return ((EnumLabel)e).getLabel(); + } + return null; + } + + /** + * Is valid label? + * + * @param label String + * @return true => valid, false => does not exist for this enum + */ + public boolean validLabel(String label) + { + return fromLabel(label) == null ? false : true; + } + + /** + * Gets enum from label + * + * @param label String + * @return enum (or null, if no enum has specified label) + */ + public E fromLabel(String label) + { + return labelMap.get(label); + } + + /** + * Gets enum from label + * + * NOTE: If specified label is invalid, the default enum is returned + * + * @param label String + * @return enum (or default enum, if label is invalid) + */ + public E toEnum(String label) + { + E e = (label == null) ? null : fromLabel(label); + if (e == null) + { + e = defaultEnum; + } + return e; + } +} diff --git a/src/main/java/org/alfresco/opencmis/EnumLabel.java b/src/main/java/org/alfresco/opencmis/EnumLabel.java new file mode 100644 index 0000000..edb1454 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/EnumLabel.java @@ -0,0 +1,41 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis; + +/** + * Enum label (mapping from Enum name to CMIS name) + * + * @author davidc + */ +public interface EnumLabel +{ + /** + * Gets the enum label (as defined by CMIS) + * + * @return enum label + */ + public String getLabel(); +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/AbstractTypeDefinitionWrapper.java b/src/main/java/org/alfresco/opencmis/dictionary/AbstractTypeDefinitionWrapper.java new file mode 100644 index 0000000..69131de --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/AbstractTypeDefinitionWrapper.java @@ -0,0 +1,813 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.opencmis.mapping.CMISMapping; +import org.alfresco.repo.dictionary.IndexTokenisationMode; +import org.alfresco.repo.dictionary.constraint.ListOfValuesConstraint; +import org.alfresco.repo.dictionary.constraint.NumericRangeConstraint; +import org.alfresco.repo.dictionary.constraint.StringLengthConstraint; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.Constraint; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO8601DateFormat; +import org.alfresco.util.ISO9075; +import org.apache.chemistry.opencmis.commons.PropertyIds; +import org.apache.chemistry.opencmis.commons.definitions.Choice; +import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition; +import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition; +import org.apache.chemistry.opencmis.commons.enums.Action; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.enums.Cardinality; +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; +import org.apache.chemistry.opencmis.commons.enums.PropertyType; +import org.apache.chemistry.opencmis.commons.enums.Updatability; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractPropertyDefinition; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractTypeDefinition; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.ChoiceImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyBooleanDefinitionImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDateTimeDefinitionImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDecimalDefinitionImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyHtmlDefinitionImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIdDefinitionImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerDefinitionImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringDefinitionImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyUriDefinitionImpl; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Base class for type definition wrappers. + * + * @author florian.mueller + */ +public abstract class AbstractTypeDefinitionWrapper implements TypeDefinitionWrapper, Serializable +{ + private static final long serialVersionUID = 1L; + private Log logger = LogFactory.getLog(AbstractTypeDefinitionWrapper.class); + + protected AbstractTypeDefinition typeDef; + protected AbstractTypeDefinition typeDefInclProperties; + + protected TypeDefinitionWrapper parent; +// protected List children; + + private String tenantId; + private ReadWriteLock lock = new ReentrantReadWriteLock(); + + @Override + public String getTenantId() + { + return tenantId; + } + + protected QName alfrescoName = null; + protected QName alfrescoClass = null; + protected Map actionEvaluators; + + protected Map propertiesById = new HashMap(); + protected Map propertiesByQueryName = new HashMap(); + protected Map propertiesByQName = new HashMap(); + + // interface + + public TypeDefinition getTypeDefinition(boolean includePropertyDefinitions) + { + lock.readLock().lock(); + try + { + if (includePropertyDefinitions) + { + return typeDefInclProperties; + } + else + { + return typeDef; + } + } + finally + { + lock.readLock().unlock(); + } + } + + protected void setTypeDefinition(AbstractTypeDefinition typeDef, AbstractTypeDefinition typeDefInclProperties) + { + this.typeDef = typeDef; + this.typeDefInclProperties = typeDefInclProperties; + } + + @Override + public String getTypeId() + { + return typeDef.getId(); + } + + @Override + public BaseTypeId getBaseTypeId() + { + return typeDef.getBaseTypeId(); + } + + @Override + public boolean isBaseType() + { + return typeDef.getId().equals(typeDef.getBaseTypeId().value()); + } + + @Override + public QName getAlfrescoName() + { + return alfrescoName; + } + + @Override + public QName getAlfrescoClass() + { + return alfrescoClass; + } + + @Override + public TypeDefinitionWrapper getParent() + { + return parent; + } + +// @Override +// public List getChildren() +// { +// return children; +// } + + @Override + public Map getActionEvaluators() + { + return actionEvaluators; + } + + @Override + public Collection getProperties() + { + return propertiesById.values(); + } + + @Override + public PropertyDefinitionWrapper getPropertyById(String propertyId) + { + return propertiesById.get(propertyId); + } + + @Override + public PropertyDefinitionWrapper getPropertyByQueryName(String queryName) + { + return propertiesByQueryName.get(queryName); + } + + @Override + public PropertyDefinitionWrapper getPropertyByQName(QName name) + { + return propertiesByQName.get(name); + } + + @Override + public void updateDefinition(DictionaryService dictionaryService) + { + String name = null; + String description = null; + ClassDefinition definition = dictionaryService.getClass(alfrescoName); + + if (definition != null) + { + name = definition.getTitle(dictionaryService); + description = definition.getDescription(dictionaryService); + } + setTypeDefDisplayName(name); + setTypeDefDescription(description); + } + + public void updateProperties(DictionaryService dictionaryService) + { + for (PropertyDefinitionWrapper propertyDefWrap : propertiesById.values()) + { + updateProperty(dictionaryService, propertyDefWrap); + } + } + + public void updateProperty(DictionaryService dictionaryService, PropertyDefinitionWrapper propertyDefWrap) + { + if (propertyDefWrap != null && propertyDefWrap.getPropertyDefinition().getDisplayName() == null) + { + AbstractPropertyDefinition property = (AbstractPropertyDefinition) propertyDefWrap.getPropertyDefinition(); + org.alfresco.service.cmr.dictionary.PropertyDefinition propDef = dictionaryService + .getProperty(QName.createQName(property.getLocalNamespace(), property.getLocalName())); + if (propDef != null) + { + String displayName = propDef.getTitle(dictionaryService); + String description = propDef.getDescription(dictionaryService); + property.setDisplayName(displayName == null ? property.getId() : displayName); + property.setDescription(description == null ? property.getDisplayName() : description); + } + } + } + + public void updateTypeDefInclProperties() + { + for (PropertyDefinition property : typeDefInclProperties.getPropertyDefinitions().values()) + { + if (property.getDisplayName() == null) + { + typeDefInclProperties.addPropertyDefinition(getPropertyById(property.getId()).getPropertyDefinition()); + } + } + } + + public void setTypeDefDisplayName(String name) + { + lock.writeLock().lock(); + try + { + typeDef.setDisplayName(name != null ? name : typeDef.getId()); + typeDefInclProperties.setDisplayName(name != null ? name : typeDef.getId()); + } + finally + { + lock.writeLock().unlock(); + } + } + + public void setTypeDefDescription(String desc) + { + lock.writeLock().lock(); + try + { + typeDef.setDescription(desc != null ? desc : typeDef.getId()); + typeDefInclProperties.setDescription(desc != null ? desc : typeDef.getId()); + } + finally + { + lock.writeLock().unlock(); + } + } + + // create + + public abstract List connectParentAndSubTypes(CMISMapping cmisMapping, CMISDictionaryRegistry registry, + DictionaryService dictionaryService); + + public abstract void resolveInheritance(CMISMapping cmisMapping, CMISDictionaryRegistry registry, + DictionaryService dictionaryService); + + public void assertComplete() + { + if (typeDef == null) + throw new IllegalStateException("typeDef is not set"); + if (typeDefInclProperties == null) + throw new IllegalStateException("typeDefInclProperties is not set"); + if (alfrescoName == null) + throw new IllegalStateException("alfrescoName is not set"); + if (alfrescoClass == null) + throw new IllegalStateException("alfrescoClass is not set"); + if (propertiesById == null) + throw new IllegalStateException("propertiesById is not set"); + if (propertiesByQueryName == null) + throw new IllegalStateException("propertiesByQueryName is not set"); + if (propertiesByQName == null) + throw new IllegalStateException("propertiesByQName is not set"); +// if (propertiesById.size() == 0) +// throw new IllegalStateException("property map empty"); + if (propertiesById.size() != propertiesByQueryName.size()) + throw new IllegalStateException("property map mismatch"); + if (propertiesById.size() != propertiesByQName.size()) + throw new IllegalStateException("property map mismatch"); + } + + /** + * Adds all property definitions owned by that type. + */ + protected void createOwningPropertyDefinitions(CMISMapping cmisMapping, + PropertyAccessorMapping propertyAccessorMapping, PropertyLuceneBuilderMapping luceneBuilderMapping, + DictionaryService dictionaryService, ClassDefinition cmisClassDef) + { + PropertyDefinition propertyDefintion; + + for (org.alfresco.service.cmr.dictionary.PropertyDefinition alfrescoPropDef : cmisClassDef.getProperties() + .values()) + { + if (!isBaseType()) + { + if (!alfrescoPropDef.getContainerClass().equals(cmisClassDef)) + { + continue; + } + } + + // compile property id + String propertyId = cmisMapping.buildPrefixEncodedString(alfrescoPropDef.getName()); + + if(propertyId.equals("cmis:secondaryObjectTypeIds") && cmisMapping.getCmisVersion() == CmisVersion.CMIS_1_0) + { + continue; + } + + // create property definition + propertyDefintion = createPropertyDefinition(cmisMapping, propertyId, alfrescoPropDef.getName(), + dictionaryService, alfrescoPropDef, false); + + // if the datatype is not supported, the property defintion will be + // null + if (propertyDefintion != null) + { + CMISPropertyAccessor propertyAccessor = null; + if (propertyAccessorMapping != null) + { + propertyAccessor = propertyAccessorMapping.getPropertyAccessor(propertyId); + if (propertyAccessor == null) + { + propertyAccessor = propertyAccessorMapping.createDirectPropertyAccessor(propertyId, + alfrescoPropDef.getName()); + } + } + + CMISPropertyLuceneBuilder luceneBuilder = null; + if (luceneBuilderMapping != null) + { + luceneBuilder = luceneBuilderMapping.getPropertyLuceneBuilder(propertyId); + if (luceneBuilder == null) + { + luceneBuilder = luceneBuilderMapping.createDirectPropertyLuceneBuilder(alfrescoPropDef + .getName()); + } + } + + registerProperty(new BasePropertyDefintionWrapper(propertyDefintion, alfrescoPropDef.getName(), this, + propertyAccessor, luceneBuilder)); + } + } + } + + /** + * Registers a property definition with this type + */ + protected void registerProperty(PropertyDefinitionWrapper propDefWrapper) + { + if (propDefWrapper == null) + { + return; + } + + if (propertiesById.containsKey(propDefWrapper.getPropertyId())) + { + throw new AlfrescoRuntimeException("Property defintion " + propDefWrapper.getPropertyId() + + " already exists on type " + typeDef.getId()); + } + + propertiesById.put(propDefWrapper.getPropertyId(), propDefWrapper); + propertiesByQueryName.put(propDefWrapper.getPropertyDefinition().getQueryName(), propDefWrapper); + propertiesByQName.put(propDefWrapper.getAlfrescoName(), propDefWrapper); + typeDefInclProperties.addPropertyDefinition(propDefWrapper.getPropertyDefinition()); + } + + /** + * Creates a property definition object. + */ + protected PropertyDefinition createPropertyDefinition(CMISMapping cmisMapping, String id, + QName alfrescoPropName, DictionaryService dictionaryService, org.alfresco.service.cmr.dictionary.PropertyDefinition propDef, boolean inherited) + { + PropertyType datatype = cmisMapping.getDataType(propDef.getDataType()); + if (datatype == null) + { + return null; + } + + AbstractPropertyDefinition result = null; + + switch (datatype) + { + case BOOLEAN: + result = new PropertyBooleanDefinitionImpl(); + break; + case DATETIME: + result = new PropertyDateTimeDefinitionImpl(); + break; + case DECIMAL: + result = new PropertyDecimalDefinitionImpl(); + break; + case HTML: + result = new PropertyHtmlDefinitionImpl(); + break; + case ID: + result = new PropertyIdDefinitionImpl(); + break; + case INTEGER: + result = new PropertyIntegerDefinitionImpl(); + break; + case STRING: + result = new PropertyStringDefinitionImpl(); + break; + case URI: + result = new PropertyUriDefinitionImpl(); + break; + default: + throw new RuntimeException("Unknown datatype! Spec change?"); + } + + if (id.equals(PropertyIds.OBJECT_TYPE_ID) || id.equals(PropertyIds.SOURCE_ID) + || id.equals(PropertyIds.TARGET_ID)) + { + // the CMIS spec requirement + result.setUpdatability(Updatability.ONCREATE); + } else + { + result.setUpdatability(propDef.isProtected() ? Updatability.READONLY : Updatability.READWRITE); + } + + result.setId(id); + result.setLocalName(alfrescoPropName.getLocalName()); + result.setLocalNamespace(alfrescoPropName.getNamespaceURI()); + result.setDisplayName(null); + result.setDescription(null); + result.setPropertyType(datatype); + result.setCardinality(propDef.isMultiValued() ? Cardinality.MULTI : Cardinality.SINGLE); + result.setIsInherited(inherited); + result.setIsRequired(propDef.isMandatory()); + addDefaultValue(propDef.getDefaultValue(), result); + + // query and order + result.setQueryName(ISO9075.encodeSQL(cmisMapping.buildPrefixEncodedString(alfrescoPropName))); + result.setIsQueryable(propDef.isIndexed()); + result.setIsOrderable(false); + + if (result.isQueryable()) + { + if (result.getCardinality() == Cardinality.SINGLE) + { + IndexTokenisationMode indexTokenisationMode = IndexTokenisationMode.TRUE; + if (propDef.getIndexTokenisationMode() != null) + { + indexTokenisationMode = propDef.getIndexTokenisationMode(); + } + + switch (indexTokenisationMode) + { + case BOTH: + case FALSE: + result.setIsOrderable(true); + break; + case TRUE: + default: + if (propDef.getDataType().getName().equals(DataTypeDefinition.BOOLEAN) + || propDef.getDataType().getName().equals(DataTypeDefinition.DATE) + || propDef.getDataType().getName().equals(DataTypeDefinition.DATETIME) + || propDef.getDataType().getName().equals(DataTypeDefinition.DOUBLE) + || propDef.getDataType().getName().equals(DataTypeDefinition.FLOAT) + || propDef.getDataType().getName().equals(DataTypeDefinition.INT) + || propDef.getDataType().getName().equals(DataTypeDefinition.LONG) + || propDef.getDataType().getName().equals(DataTypeDefinition.PATH) + ) + { + result.setIsOrderable(true); + } + } + } + } + + // MNT-9089 fix, set min/max values for numeric properties + // MNT-11304 fix, use default boundaries only for numeric types + if (result instanceof PropertyIntegerDefinitionImpl) + { + if (propDef.getDataType().getName().equals(DataTypeDefinition.INT)) + { + ((PropertyIntegerDefinitionImpl) result).setMinValue(BigInteger.valueOf(Integer.MIN_VALUE)); + ((PropertyIntegerDefinitionImpl) result).setMaxValue(BigInteger.valueOf(Integer.MAX_VALUE)); + } + if (propDef.getDataType().getName().equals(DataTypeDefinition.LONG)) + { + ((PropertyIntegerDefinitionImpl) result).setMinValue(BigInteger.valueOf(Long.MIN_VALUE)); + ((PropertyIntegerDefinitionImpl) result).setMaxValue(BigInteger.valueOf(Long.MAX_VALUE)); + } + } + // end MNT-9089 + + // constraints and choices + for (ConstraintDefinition constraintDef : propDef.getConstraints()) + { + Constraint constraint = constraintDef.getConstraint(); + if (constraint instanceof ListOfValuesConstraint) + { + addChoiceList((ListOfValuesConstraint) constraint, result); + } + + if ((constraint instanceof StringLengthConstraint) && (result instanceof PropertyStringDefinitionImpl)) + { + StringLengthConstraint slc = (StringLengthConstraint) constraint; + ((PropertyStringDefinitionImpl) result).setMaxLength(BigInteger.valueOf(slc.getMaxLength())); + } + + if (constraint instanceof NumericRangeConstraint) + { + NumericRangeConstraint nrc = (NumericRangeConstraint) constraint; + if (result instanceof PropertyIntegerDefinitionImpl) + { + ((PropertyIntegerDefinitionImpl) result) + .setMinValue(BigInteger.valueOf(((Double) nrc.getMinValue()).longValue())); + ((PropertyIntegerDefinitionImpl) result) + .setMaxValue(BigInteger.valueOf(((Double) nrc.getMaxValue()).longValue())); + } + if (result instanceof PropertyDecimalDefinitionImpl) + { + ((PropertyDecimalDefinitionImpl) result).setMinValue(BigDecimal.valueOf(nrc.getMinValue())); + ((PropertyDecimalDefinitionImpl) result).setMaxValue(BigDecimal.valueOf(nrc.getMaxValue())); + } + } + } + + return result; + } + + protected void createActionEvaluators(PropertyAccessorMapping propertyAccessorMapping, BaseTypeId baseTypeId) + { + if (propertyAccessorMapping != null) + { + actionEvaluators = propertyAccessorMapping.getActionEvaluators(baseTypeId); + } else + { + actionEvaluators = Collections.emptyMap(); + } + } + + @SuppressWarnings("unchecked") + private T convertValueFromString(String value, PropertyType datatype) + { + if (value == null) + { + return null; + } + + try + { + switch (datatype) + { + case BOOLEAN: + return (T) Boolean.valueOf(value); + case DATETIME: + GregorianCalendar cal = new GregorianCalendar(); + cal.setTime(ISO8601DateFormat.parse(value)); + return (T) cal; + case DECIMAL: + return (T) new BigDecimal(value); + case HTML: + return (T) value; + case ID: + return (T) value; + case INTEGER: + return (T) new BigInteger(value); + case STRING: + return (T) value; + case URI: + return (T) value; + default: ; + } + } + catch (Exception e) + { + logger.error("Failed to convert value " + value + " to " + datatype, e); + return null; + } + + throw new RuntimeException("Unknown datatype! Spec change?"); + } + + /** + * Adds the default value to a property definition. + */ + private void addDefaultValue(String value, PropertyDefinition propDef) + { + if (value == null) + { + return; + } + + if (propDef instanceof PropertyBooleanDefinitionImpl) + { + PropertyBooleanDefinitionImpl propDefImpl = (PropertyBooleanDefinitionImpl) propDef; + propDefImpl.setDefaultValue(Collections.singletonList((Boolean) convertValueFromString(value, + PropertyType.BOOLEAN))); + } else if (propDef instanceof PropertyDateTimeDefinitionImpl) + { + PropertyDateTimeDefinitionImpl propDefImpl = (PropertyDateTimeDefinitionImpl) propDef; + propDefImpl.setDefaultValue(Collections.singletonList((GregorianCalendar) convertValueFromString(value, + PropertyType.DATETIME))); + } else if (propDef instanceof PropertyDecimalDefinitionImpl) + { + PropertyDecimalDefinitionImpl propDefImpl = (PropertyDecimalDefinitionImpl) propDef; + propDefImpl.setDefaultValue(Collections.singletonList((BigDecimal) convertValueFromString(value, + PropertyType.DECIMAL))); + } else if (propDef instanceof PropertyHtmlDefinitionImpl) + { + PropertyHtmlDefinitionImpl propDefImpl = (PropertyHtmlDefinitionImpl) propDef; + propDefImpl.setDefaultValue(Collections.singletonList((String) convertValueFromString(value, + PropertyType.HTML))); + } else if (propDef instanceof PropertyIdDefinitionImpl) + { + PropertyIdDefinitionImpl propDefImpl = (PropertyIdDefinitionImpl) propDef; + propDefImpl.setDefaultValue(Collections.singletonList((String) convertValueFromString(value, + PropertyType.ID))); + } else if (propDef instanceof PropertyIntegerDefinitionImpl) + { + PropertyIntegerDefinitionImpl propDefImpl = (PropertyIntegerDefinitionImpl) propDef; + propDefImpl.setDefaultValue(Collections.singletonList((BigInteger) convertValueFromString(value, + PropertyType.INTEGER))); + } else if (propDef instanceof PropertyStringDefinitionImpl) + { + PropertyStringDefinitionImpl propDefImpl = (PropertyStringDefinitionImpl) propDef; + propDefImpl.setDefaultValue(Collections.singletonList((String) convertValueFromString(value, + PropertyType.STRING))); + } else if (propDef instanceof PropertyUriDefinitionImpl) + { + PropertyUriDefinitionImpl propDefImpl = (PropertyUriDefinitionImpl) propDef; + propDefImpl.setDefaultValue(Collections.singletonList((String) convertValueFromString(value, + PropertyType.URI))); + } + } + + /** + * Adds choices to the property defintion. + */ + private void addChoiceList(ListOfValuesConstraint lovc, PropertyDefinition propDef) + { + if (propDef instanceof PropertyBooleanDefinitionImpl) + { + PropertyBooleanDefinitionImpl propDefImpl = (PropertyBooleanDefinitionImpl) propDef; + propDefImpl.setIsOpenChoice(false); + + List> choiceList = new ArrayList>(); + for (String allowed : lovc.getAllowedValues()) + { + ChoiceImpl choice = new ChoiceImpl(); + choice.setDisplayName(allowed); + choice.setValue(Collections.singletonList((Boolean) convertValueFromString(allowed, + PropertyType.BOOLEAN))); + + choiceList.add(choice); + } + + propDefImpl.setChoices(choiceList); + } else if (propDef instanceof PropertyDateTimeDefinitionImpl) + { + PropertyDateTimeDefinitionImpl propDefImpl = (PropertyDateTimeDefinitionImpl) propDef; + propDefImpl.setIsOpenChoice(false); + + List> choiceList = new ArrayList>(); + for (String allowed : lovc.getAllowedValues()) + { + ChoiceImpl choice = new ChoiceImpl(); + choice.setDisplayName(allowed); + choice.setValue(Collections.singletonList((GregorianCalendar) convertValueFromString(allowed, + PropertyType.DATETIME))); + + choiceList.add(choice); + } + + propDefImpl.setChoices(choiceList); + } else if (propDef instanceof PropertyDecimalDefinitionImpl) + { + PropertyDecimalDefinitionImpl propDefImpl = (PropertyDecimalDefinitionImpl) propDef; + propDefImpl.setIsOpenChoice(false); + + List> choiceList = new ArrayList>(); + for (String allowed : lovc.getAllowedValues()) + { + ChoiceImpl choice = new ChoiceImpl(); + choice.setDisplayName(allowed); + choice.setValue(Collections.singletonList((BigDecimal) convertValueFromString(allowed, + PropertyType.DECIMAL))); + + choiceList.add(choice); + } + + propDefImpl.setChoices(choiceList); + } else if (propDef instanceof PropertyHtmlDefinitionImpl) + { + PropertyHtmlDefinitionImpl propDefImpl = (PropertyHtmlDefinitionImpl) propDef; + propDefImpl.setIsOpenChoice(false); + + List> choiceList = new ArrayList>(); + for (String allowed : lovc.getAllowedValues()) + { + ChoiceImpl choice = new ChoiceImpl(); + choice.setDisplayName(allowed); + choice.setValue(Collections.singletonList((String) convertValueFromString(allowed, PropertyType.HTML))); + + choiceList.add(choice); + } + + propDefImpl.setChoices(choiceList); + } else if (propDef instanceof PropertyIdDefinitionImpl) + { + PropertyIdDefinitionImpl propDefImpl = (PropertyIdDefinitionImpl) propDef; + propDefImpl.setIsOpenChoice(false); + + List> choiceList = new ArrayList>(); + for (String allowed : lovc.getAllowedValues()) + { + ChoiceImpl choice = new ChoiceImpl(); + choice.setDisplayName(allowed); + choice.setValue(Collections.singletonList((String) convertValueFromString(allowed, PropertyType.ID))); + + choiceList.add(choice); + } + + propDefImpl.setChoices(choiceList); + } else if (propDef instanceof PropertyIntegerDefinitionImpl) + { + PropertyIntegerDefinitionImpl propDefImpl = (PropertyIntegerDefinitionImpl) propDef; + propDefImpl.setIsOpenChoice(false); + + List> choiceList = new ArrayList>(); + for (String allowed : lovc.getAllowedValues()) + { + ChoiceImpl choice = new ChoiceImpl(); + choice.setDisplayName(allowed); + choice.setValue(Collections.singletonList((BigInteger) convertValueFromString(allowed, + PropertyType.INTEGER))); + + choiceList.add(choice); + } + + propDefImpl.setChoices(choiceList); + } else if (propDef instanceof PropertyStringDefinitionImpl) + { + PropertyStringDefinitionImpl propDefImpl = (PropertyStringDefinitionImpl) propDef; + propDefImpl.setIsOpenChoice(false); + + List> choiceList = new ArrayList>(); + for (String allowed : lovc.getAllowedValues()) + { + ChoiceImpl choice = new ChoiceImpl(); + choice.setDisplayName(allowed); + choice.setValue(Collections + .singletonList((String) convertValueFromString(allowed, PropertyType.STRING))); + + choiceList.add(choice); + } + + propDefImpl.setChoices(choiceList); + } else if (propDef instanceof PropertyUriDefinitionImpl) + { + PropertyUriDefinitionImpl propDefImpl = (PropertyUriDefinitionImpl) propDef; + propDefImpl.setIsOpenChoice(false); + + List> choiceList = new ArrayList>(); + for (String allowed : lovc.getAllowedValues()) + { + ChoiceImpl choice = new ChoiceImpl(); + choice.setDisplayName(allowed); + choice.setValue(Collections.singletonList((String) convertValueFromString(allowed, PropertyType.URI))); + + choiceList.add(choice); + } + + propDefImpl.setChoices(choiceList); + } + } +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/BasePropertyDefintionWrapper.java b/src/main/java/org/alfresco/opencmis/dictionary/BasePropertyDefintionWrapper.java new file mode 100644 index 0000000..b96b166 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/BasePropertyDefintionWrapper.java @@ -0,0 +1,89 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.io.Serializable; + +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition; + +public class BasePropertyDefintionWrapper implements PropertyDefinitionWrapper, Serializable +{ + private static final long serialVersionUID = 1L; + + private PropertyDefinition propDef; + private QName alfrescoName; + private TypeDefinitionWrapper owningType; + private CMISPropertyAccessor accessor; + private CMISPropertyLuceneBuilder luceneBuilder; + + public BasePropertyDefintionWrapper(PropertyDefinition propDef, QName alfrescoName, + TypeDefinitionWrapper owningType, CMISPropertyAccessor accessor, CMISPropertyLuceneBuilder luceneBuilder) + { + this.propDef = propDef; + this.alfrescoName = alfrescoName; + this.owningType = owningType; + this.accessor = accessor; + this.luceneBuilder = luceneBuilder; + } + + @Override + public PropertyDefinition getPropertyDefinition() + { + return propDef; + } + + @Override + public String getPropertyId() + { + return propDef.getId(); + } + + @Override + public QName getAlfrescoName() + { + return alfrescoName; + } + + @Override + public TypeDefinitionWrapper getOwningType() + { + return owningType; + } + + @Override + public CMISPropertyAccessor getPropertyAccessor() + { + return accessor; + } + + @Override + public CMISPropertyLuceneBuilder getPropertyLuceneBuilder() + { + return luceneBuilder; + } + +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/CMISAbstractDictionaryService.java b/src/main/java/org/alfresco/opencmis/dictionary/CMISAbstractDictionaryService.java new file mode 100644 index 0000000..a7c1251 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/CMISAbstractDictionaryService.java @@ -0,0 +1,507 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +import org.alfresco.opencmis.mapping.CMISMapping; +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.dictionary.CompiledModel; +import org.alfresco.repo.dictionary.DictionaryDAO; +import org.alfresco.repo.dictionary.ExtendedDictionaryListener; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO9075; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.enums.PropertyType; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationEvent; +import org.springframework.extensions.surf.util.AbstractLifecycleBean; + +/** + * Common CMIS Dictionary Support including registry of Types. + * + * @author davidc + * @author florian.mueller + */ +public abstract class CMISAbstractDictionaryService extends AbstractLifecycleBean implements CMISDictionaryService, + ExtendedDictionaryListener +{ + // Logger + protected static final Log logger = LogFactory.getLog(CMISAbstractDictionaryService.class); + + // service dependencies + protected DictionaryDAO dictionaryDAO; + protected DictionaryService dictionaryService; + protected CMISMapping cmisMapping; + protected PropertyAccessorMapping accessorMapping; + protected PropertyLuceneBuilderMapping luceneBuilderMapping; + protected TenantService tenantService; + + private final ReentrantReadWriteLock registryLock = new ReentrantReadWriteLock(); + private final WriteLock registryWriteLock = registryLock.writeLock(); + private final ReadLock registryReadLock = registryLock.readLock(); + + // note: cache is tenant-aware (if using TransctionalCache impl) + private SimpleCache singletonCache; // eg. for openCmisDictionaryRegistry + private final String KEY_OPENCMIS_DICTIONARY_REGISTRY = "key.openCmisDictionaryRegistry"; + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + /** + * Set the mapping service + * + * @param cmisMapping CMISMapping + */ + public void setCmisMapping(CMISMapping cmisMapping) + { + this.cmisMapping = cmisMapping; + } + + /** + * Set the property accessor mapping service + * + * @param accessorMapping mapping + */ + public void setPropertyAccessorMapping(PropertyAccessorMapping accessorMapping) + { + this.accessorMapping = accessorMapping; + } + + /** + * Set the property lucene mapping service + * + * @param luceneBuilderMapping mapping + */ + public void setPropertyLuceneBuilderMapping(PropertyLuceneBuilderMapping luceneBuilderMapping) + { + this.luceneBuilderMapping = luceneBuilderMapping; + } + + /** + * Set the dictionary Service + * + * @param dictionaryService DictionaryService + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * Set the dictionary DAO + * + * @param dictionaryDAO DictionaryDAO + */ + public void setDictionaryDAO(DictionaryDAO dictionaryDAO) + { + this.dictionaryDAO = dictionaryDAO; + } + + public void setSingletonCache(SimpleCache singletonCache) + { + this.singletonCache = singletonCache; + } + + protected interface DictionaryInitializer + { + Collection createDefinitions(CMISDictionaryRegistry cmisRegistry); + Collection createDefinitions(CMISDictionaryRegistry cmisRegistry, CompiledModel model); + } + + protected abstract DictionaryInitializer getCoreDictionaryInitializer(); + protected abstract DictionaryInitializer getTenantDictionaryInitializer(); + + protected CMISDictionaryRegistry getRegistry() + { + String tenant = TenantUtil.getCurrentDomain(); + return getRegistry(tenant); + } + + CMISDictionaryRegistry getRegistry(String tenant) + { + CMISDictionaryRegistry cmisRegistry = null; + boolean readLockReleased = false; + //Make sure that DictionaryRegistry exist + dictionaryDAO.getDictionaryRegistry(tenant); + + registryReadLock.lock(); + try + { + String cacheKey = getCacheKey(tenant); + cmisRegistry = singletonCache.get(cacheKey); + if(cmisRegistry == null) + { + registryReadLock.unlock(); + readLockReleased = true; + + registryWriteLock.lock(); + try + { + cmisRegistry = singletonCache.get(cacheKey); + if(cmisRegistry == null) + { + cmisRegistry = createDictionaryRegistry(tenant); + } + } + finally + { + registryWriteLock.unlock(); + } + } + } + finally + { + if(!readLockReleased) + { + registryReadLock.unlock(); + } + } + + return cmisRegistry; + } + + /* + * (non-Javadoc) + * + * @see + * org.springframework.extensions.surf.util.AbstractLifecycleBean#onBootstrap + * (org.springframework.context.ApplicationEvent) + */ + @Override + protected void onBootstrap(ApplicationEvent event) + { + afterDictionaryInit(); + + // TODO revisit (for KS and/or 1.1) + if (dictionaryDAO != null) + { + dictionaryDAO.registerListener(this); + } + else + { + logger.error("DictionaryDAO is null - hence CMIS Dictionary not registered for updates"); + } + } + + /* + * (non-Javadoc) + * + * @see + * org.springframework.extensions.surf.util.AbstractLifecycleBean#onShutdown + * (org.springframework.context.ApplicationEvent) + */ + @Override + protected void onShutdown(ApplicationEvent event) + { + } + + private String getCacheKey() + { + String tenant = tenantService.getCurrentUserDomain(); + return getCacheKey(tenant); + } + + private String getCacheKey(String tenant) + { + String cacheKey = KEY_OPENCMIS_DICTIONARY_REGISTRY + "." + tenant + "." + cmisMapping.getCmisVersion().toString(); + return cacheKey; + } + + protected CMISDictionaryRegistry createCoreDictionaryRegistry() + { + CMISDictionaryRegistryImpl cmisRegistry = new CMISDictionaryRegistryImpl(this, cmisMapping, dictionaryService, + getCoreDictionaryInitializer()); + cmisRegistry.init(); + return cmisRegistry; + } + + protected CMISDictionaryRegistry createTenantDictionaryRegistry(String tenant) + { + CMISDictionaryRegistryImpl cmisRegistry = new CMISDictionaryRegistryImpl(this, tenant, "", + cmisMapping, dictionaryService, getTenantDictionaryInitializer()); + cmisRegistry.init(); + return cmisRegistry; + } + + protected CMISDictionaryRegistry createDictionaryRegistryWithWriteLock() + { + CMISDictionaryRegistry cmisRegistry = null; + + String tenant = TenantUtil.getCurrentDomain(); + + registryWriteLock.lock(); + try + { + cmisRegistry = createDictionaryRegistry(tenant); + } + finally + { + registryWriteLock.unlock(); + } + + return cmisRegistry; + } + + protected CMISDictionaryRegistry createDictionaryRegistry(String tenant) + { + CMISDictionaryRegistry cmisRegistry = null; + String cacheKey = getCacheKey(tenant); + + if(tenant.equals(TenantService.DEFAULT_DOMAIN)) + { + cmisRegistry = createCoreDictionaryRegistry(); + } + else + { + cmisRegistry = createTenantDictionaryRegistry(tenant); + } + + // publish new registry + singletonCache.put(cacheKey, cmisRegistry); + + return cmisRegistry; + } + + @Override + public TypeDefinitionWrapper findType(String typeId) + { + TypeDefinitionWrapper typeDef = getRegistry().getTypeDefByTypeId(typeId); + if (typeDef != null && typeDef.getTypeDefinition(false).getDisplayName() == null) + { + typeDef.updateDefinition(dictionaryService); + } + return typeDef; + } + + @Override + public boolean isExcluded(QName qname) + { + return cmisMapping.isExcluded(qname); + } + + @Override + public TypeDefinitionWrapper findTypeForClass(QName clazz, BaseTypeId... matchingScopes) + { + // searching for relationship + boolean scopeByRelationship = false; + for (BaseTypeId scope : matchingScopes) + { + if (scope == BaseTypeId.CMIS_RELATIONSHIP) + { + scopeByRelationship = true; + break; + } + } + + // locate type in registry + clazz = cmisMapping.getCmisType(clazz); + TypeDefinitionWrapper typeDef = null; + if (scopeByRelationship) + { + typeDef = getRegistry().getAssocDefByQName(clazz); + } + else + { + typeDef = getRegistry().getTypeDefByQName(clazz); + if (typeDef == null) + { + typeDef = getRegistry().getAssocDefByQName(clazz); + } + } + + // ensure matches one of provided matching scopes + TypeDefinitionWrapper matchingTypeDef = (matchingScopes.length == 0) ? typeDef : null; + if (typeDef != null) + { + for (BaseTypeId scope : matchingScopes) + { + if (typeDef.getBaseTypeId() == scope) + { + matchingTypeDef = typeDef; + break; + } + } + } + + return matchingTypeDef; + } + + @Override + public TypeDefinitionWrapper findNodeType(QName clazz) + { + return getRegistry().getTypeDefByQName(cmisMapping.getCmisType(clazz)); + } + + @Override + public TypeDefinitionWrapper findAssocType(QName clazz) + { + return getRegistry().getAssocDefByQName(cmisMapping.getCmisType(clazz)); + } + + @Override + public TypeDefinitionWrapper findTypeByQueryName(String queryName) + { + // ISO 9075 name look up should be lower case. + return getRegistry().getTypeDefByQueryName(ISO9075.lowerCaseEncodedSQL(queryName)); + } + + @Override + public PropertyDefinitionWrapper findProperty(String propId) + { + return getRegistry().getPropDefByPropId(propId); + } + + @Override + public PropertyDefinitionWrapper findPropertyByQueryName(String queryName) + { + return getRegistry().getPropDefByQueryName(ISO9075.lowerCaseEncodedSQL(queryName)); + } + + @Override + public List getBaseTypes() + { + return getBaseTypes(true); + } + + @Override + public List getBaseTypes(boolean includeParent) + { + List types = getRegistry().getBaseTypes(includeParent); + + for (TypeDefinitionWrapper typeDef : types) + { + if (typeDef != null && typeDef.getTypeDefinition(false).getDisplayName() == null) + { + typeDef.updateDefinition(dictionaryService); + } + } + + return Collections.unmodifiableList(types); + } + + @Override + public List getAllTypes() + { + // TODO is there a way of not having to reconstruct this every time? + return Collections.unmodifiableList(new ArrayList(getRegistry().getTypeDefs())); + } + + @Override + public List getAllTypes(boolean includeParent) + { + // TODO is there a way of not having to reconstruct this every time? + return Collections.unmodifiableList(new ArrayList(getRegistry().getTypeDefs(includeParent))); + } + + @Override + public PropertyType findDataType(QName dataType) + { + return cmisMapping.getDataType(dataType); + } + + @Override + public QName findAlfrescoDataType(PropertyType propertyType) + { + return cmisMapping.getAlfrescoDataType(propertyType); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.dictionary.DictionaryListener#onInit() + */ + @Override + public void onDictionaryInit() + { + } + + @Override + public void modelAdded(CompiledModel model, String tenantDomain) + { + getRegistry(tenantDomain).addModel(model); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.dictionary.DictionaryListener#afterInit() + */ + @Override + public void afterDictionaryInit() + { + createDictionaryRegistryWithWriteLock(); + } + + /* + * (non-Javadoc) + * + * @see + * org.alfresco.repo.dictionary.DictionaryListener#afterDictionaryDestroy() + */ + @Override + public void afterDictionaryDestroy() + { + registryWriteLock.lock(); + try + { + String cacheKey = getCacheKey(); + singletonCache.remove(cacheKey); + } + finally + { + registryWriteLock.unlock(); + } + } + + @Override + public List getChildren(String typeId) + { + List children = getRegistry().getChildren(typeId); + + for (TypeDefinitionWrapper child : children) + { + if (child != null && child.getTypeDefinition(false).getDisplayName() == null) + { + child.updateDefinition(dictionaryService); + } + } + + return children; + } +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/CMISActionEvaluator.java b/src/main/java/org/alfresco/opencmis/dictionary/CMISActionEvaluator.java new file mode 100644 index 0000000..3936234 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/CMISActionEvaluator.java @@ -0,0 +1,46 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import org.apache.chemistry.opencmis.commons.enums.Action; + +public interface CMISActionEvaluator +{ + /** + * Gets the CMIS Allowed Action + * + * @return Action + */ + public Action getAction(); + + /** + * Determines if an action is allowed on an object + * + * @param nodeInfo CMISNodeInfo + * @return boolean + */ + public boolean isAllowed(CMISNodeInfo nodeInfo); +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryRegistry.java b/src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryRegistry.java new file mode 100644 index 0000000..eb3e98c --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryRegistry.java @@ -0,0 +1,63 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.Collection; +import java.util.List; + +import org.alfresco.repo.dictionary.CompiledModel; +import org.alfresco.service.namespace.QName; + +/** + * + * @author sglover + * + */ +public interface CMISDictionaryRegistry +{ + TypeDefinitionWrapper getTypeDefByTypeId(String typeId); + TypeDefinitionWrapper getTypeDefByTypeId(String typeId, boolean includeParent); + TypeDefinitionWrapper getAssocDefByQName(QName qname); + TypeDefinitionWrapper getTypeDefByQueryName(Object queryName); + TypeDefinitionWrapper getTypeDefByQName(QName qname); + PropertyDefinitionWrapper getPropDefByPropId(String propId); + PropertyDefinitionWrapper getPropDefByQueryName(Object queryName); + List getBaseTypes(); + List getBaseTypes(boolean includeParent); + Collection getTypeDefs(); + Collection getTypeDefs(boolean includeParent); + Collection getAssocDefs(); + Collection getAssocDefs(boolean includeParent); + void registerTypeDefinition(AbstractTypeDefinitionWrapper typeDef); + String getTenant(); + List getChildren(String typeId); + void setChildren(String typeId, List children); + void addChild(String typeId, TypeDefinitionWrapper child); + + void addModel(CompiledModel model); + void updateModel(CompiledModel model); + void removeModel(CompiledModel model); +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryRegistryImpl.java b/src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryRegistryImpl.java new file mode 100644 index 0000000..3db14e8 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryRegistryImpl.java @@ -0,0 +1,538 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.opencmis.dictionary.CMISAbstractDictionaryService.DictionaryInitializer; +import org.alfresco.opencmis.mapping.CMISMapping; +import org.alfresco.repo.dictionary.CompiledModel; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.opencmis.commons.data.CmisExtensionElement; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.CmisExtensionElementImpl; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * CMIS Dictionary registry + * + * Index of CMIS Type Definitions + * + * @author sglover + */ +public class CMISDictionaryRegistryImpl implements CMISDictionaryRegistry +{ + public static final String ALFRESCO_EXTENSION_NAMESPACE = "http://www.alfresco.org"; + public static final String MANDATORY_ASPECTS = "mandatoryAspects"; + public static final String MANDATORY_ASPECT = "mandatoryAspect"; + + // Logger + protected static final Log logger = LogFactory.getLog(CMISDictionaryRegistryImpl.class); + + private CMISMapping cmisMapping; + private DictionaryService dictionaryService; + private String tenant; + + protected CMISAbstractDictionaryService cmisDictionaryService; + private String parentTenant; + + private DictionaryInitializer dictionaryInitializer; + + // Type Definitions Index + private Map typeDefsByQName = new HashMap(); + private Map assocDefsByQName = new HashMap(); + + private Map typeDefsByTypeId = new HashMap(); + private Map typeDefsByQueryName = new HashMap(); + private List baseTypes = new ArrayList(); + + private Map propDefbyPropId = new HashMap(); + private Map propDefbyQueryName = new HashMap(); + + private Map> children = new HashMap>(); + + public CMISDictionaryRegistryImpl(CMISAbstractDictionaryService cmisDictionaryService, CMISMapping cmisMapping, + DictionaryService dictionaryService, DictionaryInitializer dictionaryInitializer) + { + this(cmisDictionaryService, "", null, cmisMapping, dictionaryService, dictionaryInitializer); + } + + /* + * Testing only. + */ + CMISDictionaryRegistryImpl() + { + } + + public CMISDictionaryRegistryImpl(CMISAbstractDictionaryService cmisDictionaryService, String tenant, String parentTenant, + CMISMapping cmisMapping, DictionaryService dictionaryService, DictionaryInitializer dictionaryInitializer) + { + this.cmisDictionaryService = cmisDictionaryService; + this.tenant = tenant; + this.parentTenant = parentTenant; + this.cmisMapping = cmisMapping; + this.dictionaryService = dictionaryService; + this.dictionaryInitializer = dictionaryInitializer; + } + + protected CMISDictionaryRegistry getParent() + { + CMISDictionaryRegistry registry = null; + if(parentTenant != null) + { + return cmisDictionaryService.getRegistry(parentTenant); + } + return registry; + } + + @Override + public String getTenant() + { + return tenant; + } + + private List getChildrenImpl(String typeId) + { + return children.get(typeId); + } + + @Override + public List getChildren(String typeId) + { + List ret = new LinkedList<>(); + + List children = getChildrenImpl(typeId); + if(children != null) + { + ret.addAll(children); + } + + if(getParent() != null) + { + children = getParent().getChildren(typeId); + if(children != null) + { + ret.addAll(children); + } + } + + return ret; + } + + @Override + public void setChildren(String typeId, List children) + { + this.children.put(typeId, children); + } + + @Override + public void addChild(String typeId, TypeDefinitionWrapper child) + { + List children = this.children.get(typeId); + if(children == null) + { + children = new LinkedList(); + this.children.put(typeId, children); + } + children.add(child); + } + + @Override + public TypeDefinitionWrapper getTypeDefByTypeId(String typeId) + { + return getTypeDefByTypeId(typeId, true); + } + + @Override + public TypeDefinitionWrapper getTypeDefByTypeId(String typeId, boolean includeParent) + { + TypeDefinitionWrapper typeDef = typeDefsByTypeId.get(typeId); + if(typeDef == null && includeParent && getParent() != null) + { + typeDef = getParent().getTypeDefByTypeId(typeId); + } + + return typeDef; + } + + @Override + public TypeDefinitionWrapper getAssocDefByQName(QName qname) + { + TypeDefinitionWrapper typeDef = assocDefsByQName.get(qname); + if(typeDef == null && getParent() != null) + { + typeDef = getParent().getAssocDefByQName(qname); + } + + return typeDef; + } + + @Override + public TypeDefinitionWrapper getTypeDefByQueryName(Object queryName) + { + TypeDefinitionWrapper typeDef = typeDefsByQueryName.get(queryName); + if(typeDef == null && getParent() != null) + { + typeDef = getParent().getTypeDefByQueryName(queryName); + } + + return typeDef; + } + + @Override + public TypeDefinitionWrapper getTypeDefByQName(QName qname) + { + TypeDefinitionWrapper typeDef = typeDefsByQName.get(qname); + if(typeDef == null && getParent() != null) + { + typeDef = getParent().getTypeDefByQName(qname); + } + + return typeDef; + } + + @Override + public PropertyDefinitionWrapper getPropDefByPropId(String propId) + { + PropertyDefinitionWrapper propDef = propDefbyPropId.get(propId); + if(propDef == null && getParent() != null) + { + propDef = getParent().getPropDefByPropId(propId); + } + + return propDef; + } + + @Override + public PropertyDefinitionWrapper getPropDefByQueryName(Object queryName) + { + PropertyDefinitionWrapper propDef = propDefbyQueryName.get(queryName); + if(propDef == null && getParent() != null) + { + propDef = getParent().getPropDefByQueryName(queryName); + } + + return propDef; + } + + private Collection getTypeDefsImpl() + { + return typeDefsByTypeId.values(); + } + + @Override + public Collection getTypeDefs() + { + return getTypeDefs(true); + } + + @Override + public Collection getTypeDefs(boolean includeParent) + { + Collection ret = new LinkedList<>(); + ret.addAll(getTypeDefsImpl()); + if(includeParent && getParent() != null) + { + ret.addAll(getParent().getTypeDefs()); + } + return Collections.unmodifiableCollection(ret); + } + + private Collection getAssocDefsImpl() + { + return assocDefsByQName.values(); + } + + @Override + public Collection getAssocDefs() + { + return getAssocDefs(true); + } + + @Override + public Collection getAssocDefs(boolean includeParent) + { + Collection ret = new LinkedList<>(); + ret.addAll(getAssocDefsImpl()); + if(includeParent && getParent() != null) + { + ret.addAll(getParent().getAssocDefs()); + } + return Collections.unmodifiableCollection(ret); + } + + private void addTypeExtensions(TypeDefinitionWrapper td) + { + QName classQName = td.getAlfrescoClass(); + ClassDefinition classDef = dictionaryService.getClass(classQName); + if(classDef != null) + { + // add mandatory/default aspects + List defaultAspects = classDef.getDefaultAspects(true); + if(defaultAspects != null && defaultAspects.size() > 0) + { + List mandatoryAspectsExtensions = new ArrayList(); + for(AspectDefinition aspectDef : defaultAspects) + { + QName aspectQName = aspectDef.getName(); + + TypeDefinitionWrapper aspectType = getTypeDefByQName(cmisMapping.getCmisType(aspectQName)); + if (aspectType == null) + { + continue; + } + + mandatoryAspectsExtensions.add(new CmisExtensionElementImpl(ALFRESCO_EXTENSION_NAMESPACE, MANDATORY_ASPECT, null, aspectType.getTypeId())); + } + + if(!mandatoryAspectsExtensions.isEmpty()) + { + td.getTypeDefinition(true).setExtensions( + Collections.singletonList((CmisExtensionElement) new CmisExtensionElementImpl( + ALFRESCO_EXTENSION_NAMESPACE, MANDATORY_ASPECTS, null, mandatoryAspectsExtensions))); + } + } + } + } + + @Override + public void addModel(CompiledModel model) + { + Collection types = dictionaryInitializer.createDefinitions(this, model); + addTypes(types); + for(AbstractTypeDefinitionWrapper type : types) + { + type.resolveInheritance(cmisMapping, this, dictionaryService); + } + } + + @Override + public void updateModel(CompiledModel model) + { + // TODO + } + + @Override + public void removeModel(CompiledModel model) + { + // TODO + } + + private void clear() + { + typeDefsByQName.clear(); + assocDefsByQName.clear(); + typeDefsByTypeId.clear(); + typeDefsByQueryName.clear(); + baseTypes.clear(); + + propDefbyPropId.clear(); + propDefbyQueryName.clear(); + + children.clear(); + } + + private void addTypes(Collection types) + { + // phase 1: construct type definitions and link them together + for (AbstractTypeDefinitionWrapper objectTypeDef : types) + { + List children = objectTypeDef.connectParentAndSubTypes(cmisMapping, this, dictionaryService); + setChildren(objectTypeDef.getTypeId(), children); + } + + // phase 2: register base types and inherit property definitions + for (AbstractTypeDefinitionWrapper typeDef : types) + { + if (typeDef.getTypeDefinition(false).getParentTypeId() == null || + !tenant.equals(TenantService.DEFAULT_DOMAIN)) + { + if(tenant.equals(TenantService.DEFAULT_DOMAIN)) + { + baseTypes.add(typeDef); + } + typeDef.resolveInheritance(cmisMapping, this, dictionaryService); + } + } + + // phase 3: register properties + for (AbstractTypeDefinitionWrapper typeDef : types) + { + registerPropertyDefinitions(typeDef); + } + + // phase 4: assert valid + for (AbstractTypeDefinitionWrapper typeDef : types) + { + typeDef.assertComplete(); + + addTypeExtensions(typeDef); + } + } + + public void init() + { + long start = System.currentTimeMillis(); + + if (logger.isDebugEnabled()) + { + logger.debug("Creating type definitions..."); + } + + Collection types = dictionaryInitializer.createDefinitions(this); + addTypes(types); + + long end = System.currentTimeMillis(); + + if (logger.isInfoEnabled()) + { + logger.info("Initialized CMIS Dictionary " + cmisMapping.getCmisVersion() + " tenant " + tenant + " in " + (end - start) + "ms. Types:" + + typeDefsByTypeId.size() + ", Base Types:" + baseTypes.size()); + } + } + + private List getBaseTypesImpl() + { + return baseTypes; + } + + @Override + public List getBaseTypes() + { + return getBaseTypes(true); + } + + @Override + public List getBaseTypes(boolean includeParent) + { + List ret = new LinkedList(); + + List baseTypes = getBaseTypesImpl(); + if(baseTypes != null) + { + ret.addAll(baseTypes); + } + + if(includeParent && getParent() != null) + { + baseTypes = getParent().getBaseTypes(); + if(baseTypes != null) + { + ret.addAll(baseTypes); + } + } + + return Collections.unmodifiableList(ret); + } + + /** + * Register type definition. + * + * @param typeDef AbstractTypeDefinitionWrapper + */ + @Override + public void registerTypeDefinition(AbstractTypeDefinitionWrapper typeDef) + { + TypeDefinitionWrapper existingTypeDef = typeDefsByTypeId.get(typeDef.getTypeId()); + if (existingTypeDef != null) + { +// throw new AlfrescoRuntimeException("Type " + typeDef.getTypeId() + " already registered"); + if(logger.isWarnEnabled()) + { + logger.warn("Type " + typeDef.getTypeId() + " already registered"); + } + } + + typeDefsByTypeId.put(typeDef.getTypeId(), typeDef); + QName typeQName = typeDef.getAlfrescoName(); + if (typeQName != null) + { + if ((typeDef instanceof RelationshipTypeDefintionWrapper) && !typeDef.isBaseType()) + { + assocDefsByQName.put(typeQName, typeDef); + } else + { + typeDefsByQName.put(typeQName, typeDef); + } + } + + typeDefsByQueryName.put(typeDef.getTypeDefinition(false).getQueryName(), typeDef); + + if (logger.isDebugEnabled()) + { + logger.debug("Registered type " + typeDef.getTypeId() + " (scope=" + typeDef.getBaseTypeId() + ")"); + logger.debug(" QName: " + typeDef.getAlfrescoName()); + logger.debug(" Table: " + typeDef.getTypeDefinition(false).getQueryName()); + logger.debug(" Action Evaluators: " + typeDef.getActionEvaluators().size()); + } + } + + /** + * Register property definitions. + * + * @param typeDef AbstractTypeDefinitionWrapper + */ + public void registerPropertyDefinitions(AbstractTypeDefinitionWrapper typeDef) + { + for (PropertyDefinitionWrapper propDef : typeDef.getProperties(false)) + { + if (propDef.getPropertyDefinition().isInherited()) + { + continue; + } + + propDefbyPropId.put(propDef.getPropertyId(), propDef); + propDefbyQueryName.put(propDef.getPropertyDefinition().getQueryName(), propDef); + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + StringBuilder builder = new StringBuilder(); + builder.append("DictionaryRegistry["); + builder.append("Types=").append(typeDefsByTypeId.size()).append(", "); + builder.append("Base Types=").append(baseTypes.size()).append(", "); + builder.append("]"); + return builder.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryReload.java b/src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryReload.java new file mode 100644 index 0000000..cdaa202 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryReload.java @@ -0,0 +1,77 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationEvent; +import org.springframework.extensions.surf.util.AbstractLifecycleBean; + +/** + * Temporary workaround for: + *

    + *
  • ACE-5041: CLONE - google docs content cannot be accessed via cmis
  • + *
+ * + * TODO: Remove this bean when rework for MNT-14819 is complete. + * + * @author Matt Ward + */ +public final class CMISDictionaryReload extends AbstractLifecycleBean +{ + private static final Log log = LogFactory.getLog(CMISDictionaryReload.class); + private final CMISAbstractDictionaryService cmisDictService; + private final boolean enabled; + + public CMISDictionaryReload(CMISAbstractDictionaryService cmisDictService, boolean enabled) + { + this.cmisDictService = cmisDictService; + this.enabled = enabled; + } + + public void reload() + { + if (enabled) + { + // Avoid deadlock by making sure we already have a registry present. + cmisDictService.getRegistry(); + log.debug("Reloading CMIS dictionary."); + cmisDictService.afterDictionaryInit(); + } + } + + @Override + protected void onBootstrap(ApplicationEvent event) + { + reload(); + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + // Do nothing. + } +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryService.java b/src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryService.java new file mode 100644 index 0000000..ecdf9ab --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/CMISDictionaryService.java @@ -0,0 +1,103 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.List; + +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.enums.PropertyType; + +/** + * Service to query the CMIS meta model + * + * @author davidc + */ +public interface CMISDictionaryService +{ + /** + * Find type for type id + * + * @param typeId String + * @return TypeDefinitionWrapper + */ + TypeDefinitionWrapper findType(String typeId); + + List getChildren(String typeId); + + /** + * Find type for Alfresco class name. Optionally, constrain match to one of + * specified CMIS scopes + * + * @param clazz QName + * @param matchingScopes BaseTypeId... + * @return TypeDefinitionWrapper + */ + TypeDefinitionWrapper findTypeForClass(QName clazz, BaseTypeId... matchingScopes); + + TypeDefinitionWrapper findNodeType(QName clazz); + + TypeDefinitionWrapper findAssocType(QName clazz); + + PropertyDefinitionWrapper findProperty(String propId); + + PropertyDefinitionWrapper findPropertyByQueryName(String queryName); + + /** + * Find a type by its query name + * + * @param queryName String + * @return TypeDefinitionWrapper + */ + TypeDefinitionWrapper findTypeByQueryName(String queryName); + + /** + * Get Base Types + */ + List getBaseTypes(); + + List getBaseTypes(boolean includeParent); + + /** + * Get all Types + * + */ + List getAllTypes(); + + List getAllTypes(boolean includeParent); + + /** + * Find data type + * + * @param dataType QName + * @return PropertyType + */ + PropertyType findDataType(QName dataType); + + QName findAlfrescoDataType(PropertyType propertyType); + + boolean isExcluded(QName qname); +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/CMISNodeInfo.java b/src/main/java/org/alfresco/opencmis/dictionary/CMISNodeInfo.java new file mode 100644 index 0000000..29f2faa --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/CMISNodeInfo.java @@ -0,0 +1,104 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +public interface CMISNodeInfo +{ + String getObjectId(); + + CMISObjectVariant getObjectVariant(); + + boolean isVariant(CMISObjectVariant var); + + NodeRef getNodeRef(); + + String getCurrentNodeId(); + + NodeRef getCurrentNodeNodeRef(); + + String getCurrentObjectId(); + + boolean isCurrentVersion(); + + boolean isPWC(); + + boolean hasPWC(); + + boolean isVersion(); + + boolean isLatestVersion(); + + boolean isLatestMajorVersion(); + + boolean isMajorVersion(); + + String getVersionLabel(); + + String getCheckinComment(); + + AssociationRef getAssociationRef(); + + TypeDefinitionWrapper getType(); + + boolean isFolder(); + + boolean isRootFolder(); + + boolean isDocument(); + + boolean isRelationship(); + + boolean isItem(); + + String getName(); + + String getPath(); + + Serializable getCreationDate(); + + Serializable getModificationDate(); + + Serializable getPropertyValue(String id); + + boolean containsPropertyValue(String id); + + void putPropertyValue(String id, Serializable value); + + List getParents(); + + Map getNodeProps(); + + Set getNodeAspects(); +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/opencmis/dictionary/CMISObjectVariant.java b/src/main/java/org/alfresco/opencmis/dictionary/CMISObjectVariant.java new file mode 100644 index 0000000..cb0442f --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/CMISObjectVariant.java @@ -0,0 +1,41 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +public enum CMISObjectVariant +{ + INVALID_ID, // not a valid object id + NOT_EXISTING, // valid id but object doesn't exist + NOT_A_CMIS_OBJECT, // object is not mapped to CMIS + FOLDER, // object is a folder + ITEM, // object is an item + CURRENT_VERSION, // object is a document (current version) + VERSION, // object is a version (not updatable) + PWC, // object is a PWC + ASSOC, // object is a relationship + PERMISSION_DENIED + // user has no permissions +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/CMISPropertyAccessor.java b/src/main/java/org/alfresco/opencmis/dictionary/CMISPropertyAccessor.java new file mode 100644 index 0000000..c408577 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/CMISPropertyAccessor.java @@ -0,0 +1,80 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.io.Serializable; + +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * CMIS Property Accessor (get and set property values) + * + * @author davidc + */ +public interface CMISPropertyAccessor +{ + /** + * Get the CMIS Property Name + * + * @return String + */ + String getName(); + + /** + * Get the (directly) mapped Alfresco property (if a direct mapping exists) + * + * @return QName + */ + QName getMappedProperty(); + + /** + * Set the property value for a node + * + * @param nodeRef NodeRef + * @param value Serializable + */ + void setValue(NodeRef nodeRef, Serializable value); + + /** + * Get the property value for a node or an association + * + * @param nodeInfo CMISNodeInfo + * @return Serializable + */ + Serializable getValue(CMISNodeInfo nodeInfo); + + /** + * Creates a node info object form the given node ref. + */ + CMISNodeInfo createNodeInfo(NodeRef nodeRef); + + /** + * Creates a node info object form the given association ref. + */ + CMISNodeInfo createNodeInfo(AssociationRef assocRef); +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/CMISPropertyLuceneBuilder.java b/src/main/java/org/alfresco/opencmis/dictionary/CMISPropertyLuceneBuilder.java new file mode 100644 index 0000000..063af04 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/CMISPropertyLuceneBuilder.java @@ -0,0 +1,130 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.io.Serializable; +import java.util.Collection; + +import org.alfresco.repo.search.adaptor.lucene.LuceneFunction; +import org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor; +import org.alfresco.repo.search.impl.querymodel.PredicateMode; + + +/** + * Encapsulate the building of lucene queries for property predicates + */ +public interface CMISPropertyLuceneBuilder +{ + /** + * @param value Serializable + * @param mode PredicateMode + * @param luceneFunction LuceneFunction + * @return the query - may be null if no query is required + * @throws E + */ + public Q buildLuceneEquality(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E; + + /** + * @param not Boolean + * @return the query - may be null if no query is required + * @throws E + */ + public Q buildLuceneExists(LuceneQueryParserAdaptor lqpa, Boolean not) throws E; + + /** + * @param value Serializable + * @param mode PredicateMode + * @param luceneFunction LuceneFunction + * @return the query - may be null if no query is required + * @throws E + */ + public Q buildLuceneGreaterThan(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E; + + /** + * @param value Serializable + * @param mode PredicateMode + * @param luceneFunction LuceneFunction + * @return the query - may be null if no query is required + * @throws E + */ + public Q buildLuceneGreaterThanOrEquals(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E; + + /** + * @param not Boolean + * @param mode PredicateMode + * @return the query - may be null if no query is required + * @throws E + */ + public Q buildLuceneIn(LuceneQueryParserAdaptor lqpa, Collection values, Boolean not, PredicateMode mode) throws E; + + /** + * @param value PredicateMode + * @param mode PredicateMode + * @param luceneFunction LuceneFunction + * @return the query - may be null if no query is required + * @throws E + */ + public Q buildLuceneInequality(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E; + + /** + * @param value Serializable + * @param mode PredicateMode + * @param luceneFunction LuceneFunction + * @return the query - may be null if no query is required + * @throws E + */ + public Q buildLuceneLessThan(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E; + + /** + * @param value Serializable + * @param mode PredicateMode + * @param luceneFunction LuceneFunction + * @return the query - may be null if no query is required + * @throws E + */ + public Q buildLuceneLessThanOrEquals(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E; + + /** + * @param value Serializable + * @param not Boolean + * @return the query - may be null if no query is required + * @throws E + */ + public Q buildLuceneLike(LuceneQueryParserAdaptor lqpa, Serializable value, Boolean not) throws E; + + /** + * @param lqpa TODO + * @return the sort field + * @throws E + */ + public String getLuceneSortField(LuceneQueryParserAdaptor lqpa) throws E; + + /** + * @return the field name + * + */ + public String getLuceneFieldName(); +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/CMISStrictDictionaryService.java b/src/main/java/org/alfresco/opencmis/dictionary/CMISStrictDictionaryService.java new file mode 100644 index 0000000..610f8a8 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/CMISStrictDictionaryService.java @@ -0,0 +1,319 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.Collection; +import java.util.LinkedList; + +import org.alfresco.opencmis.mapping.CMISMapping; +import org.alfresco.repo.dictionary.CompiledModel; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * CMIS Dictionary which provides Types that strictly conform to the CMIS + * specification. + * + * That is, only maps types to one of root Document, Folder, Relationship and + * Policy. + * + * And Item which is pretty much anything that is not a Document, Folder, Relationship or Policy. + * + * @author steveglover + * @author davidc + * @author mrogers + */ +public class CMISStrictDictionaryService extends CMISAbstractDictionaryService +{ + private Log logger = LogFactory.getLog(CMISStrictDictionaryService.class); + + public static final String DEFAULT = "DEFAULT_DICTIONARY"; + + private DictionaryInitializer coreDictionaryInitializer; + private DictionaryInitializer tenantDictionaryInitializer; + + public void init() + { + this.coreDictionaryInitializer = new DictionaryInitializer() + { + @Override + public Collection createDefinitions(CMISDictionaryRegistry cmisRegistry) + { + Collection ret = new LinkedList<>(); + ret.addAll(createTypeDefs(cmisRegistry, dictionaryService.getAllTypes(true))); + + Collection assocQNames = dictionaryService.getAllAssociations(true); + + // register base type + String typeId = cmisMapping.getCmisTypeId(BaseTypeId.CMIS_RELATIONSHIP, CMISMapping.RELATIONSHIP_QNAME); + ClassDefinition classDef = dictionaryService.getClass(CMISMapping.RELATIONSHIP_QNAME); + + // from Thor + if (classDef == null) + { + if (assocQNames.size() != 0) + { + logger.warn("Unexpected - no class for "+CMISMapping.RELATIONSHIP_QNAME+" - cannot create assocDefs for: "+assocQNames); + } + } + else + { + RelationshipTypeDefintionWrapper objectTypeDef = new RelationshipTypeDefintionWrapper(cmisMapping, + accessorMapping, luceneBuilderMapping, typeId, dictionaryService, classDef); + cmisRegistry.registerTypeDefinition(objectTypeDef); + ret.add(objectTypeDef); + + ret.addAll(createAssocDefs(cmisRegistry, assocQNames)); + } + + ret.addAll(createTypeDefs(cmisRegistry, dictionaryService.getAllAspects(true))); + return ret; + } + + @Override + public Collection createDefinitions(CMISDictionaryRegistry cmisRegistry, + CompiledModel model) + { + Collection ret = new LinkedList<>(); + + model.getClass(model.getTypes().iterator().next().getName()); + + for(TypeDefinition typeDef : model.getTypes()) + { + QName classQName = typeDef.getName(); + AbstractTypeDefinitionWrapper objectTypeDef = createTypeDef(classQName); + if(objectTypeDef != null) + { + cmisRegistry.registerTypeDefinition(objectTypeDef); + ret.add(objectTypeDef); + } + } + + for(AssociationDefinition assocDef : model.getAssociations()) + { + QName classQName = assocDef.getName(); + RelationshipTypeDefintionWrapper assocTypeDef = createAssocDef(classQName); + if(assocTypeDef != null) + { + cmisRegistry.registerTypeDefinition(assocTypeDef); + ret.add(assocTypeDef); + } + } + + for(AspectDefinition aspectDef : model.getAspects()) + { + QName classQName = aspectDef.getName(); + AbstractTypeDefinitionWrapper objectTypeDef = createTypeDef(classQName); + if(objectTypeDef != null) + { + cmisRegistry.registerTypeDefinition(objectTypeDef); + ret.add(objectTypeDef); + } + } + + return ret; + } + }; + + this.tenantDictionaryInitializer = new DictionaryInitializer() + { + @Override + public Collection createDefinitions(CMISDictionaryRegistry cmisRegistry) + { + Collection ret = new LinkedList<>(); + ret.addAll(createTypeDefs(cmisRegistry, dictionaryService.getAllTypes(false))); + ret.addAll(createAssocDefs(cmisRegistry, dictionaryService.getAllAssociations(false))); + ret.addAll(createTypeDefs(cmisRegistry, dictionaryService.getAllAspects(false))); + return ret; + } + + @Override + public Collection createDefinitions(CMISDictionaryRegistry cmisRegistry, + CompiledModel model) + { + Collection ret = new LinkedList<>(); + + for(TypeDefinition typeDef : model.getTypes()) + { + QName classQName = typeDef.getName(); + AbstractTypeDefinitionWrapper objectTypeDef = createTypeDef(classQName); + if(objectTypeDef != null) + { + cmisRegistry.registerTypeDefinition(objectTypeDef); + ret.add(objectTypeDef); + } + } + + for(AssociationDefinition assocDef : model.getAssociations()) + { + QName classQName = assocDef.getName(); + RelationshipTypeDefintionWrapper assocTypeDef = createAssocDef(classQName); + if(assocTypeDef != null) + { + cmisRegistry.registerTypeDefinition(assocTypeDef); + ret.add(assocTypeDef); + } + } + + for(AspectDefinition aspectDef : model.getAspects()) + { + QName classQName = aspectDef.getName(); + AbstractTypeDefinitionWrapper objectTypeDef = createTypeDef(classQName); + if(objectTypeDef != null) + { + cmisRegistry.registerTypeDefinition(objectTypeDef); + ret.add(objectTypeDef); + } + } + + return ret; + } + }; + } + + /** + * Create Type Definitions + * + * @param classQName QName + */ + private AbstractTypeDefinitionWrapper createTypeDef(QName classQName) + { + AbstractTypeDefinitionWrapper objectTypeDef = null; + + // skip items that are remapped to CMIS model + if(!cmisMapping.isRemappedType(classQName)) + { + // create appropriate kind of type definition + ClassDefinition classDef = dictionaryService.getClass(classQName); + String typeId = null; + if (cmisMapping.isValidCmisDocument(classQName)) + { + typeId = cmisMapping.getCmisTypeId(BaseTypeId.CMIS_DOCUMENT, classQName); + objectTypeDef = new DocumentTypeDefinitionWrapper(cmisMapping, accessorMapping, luceneBuilderMapping, typeId, dictionaryService, classDef); + } + else if (cmisMapping.isValidCmisFolder(classQName)) + { + typeId = cmisMapping.getCmisTypeId(BaseTypeId.CMIS_FOLDER, classQName); + objectTypeDef = new FolderTypeDefintionWrapper(cmisMapping, accessorMapping, luceneBuilderMapping, typeId, dictionaryService, classDef); + } + else if (cmisMapping.getCmisVersion().equals(CmisVersion.CMIS_1_1) && cmisMapping.isValidCmisSecondaryType(classQName)) + { + typeId = cmisMapping.getCmisTypeId(BaseTypeId.CMIS_SECONDARY, classQName); + objectTypeDef = new SecondaryTypeDefinitionWrapper(cmisMapping, accessorMapping, luceneBuilderMapping, typeId, dictionaryService, classDef); + } + else if (cmisMapping.isValidCmisPolicy(classQName)) + { + typeId = cmisMapping.getCmisTypeId(BaseTypeId.CMIS_POLICY, classQName); + objectTypeDef = new PolicyTypeDefintionWrapper(cmisMapping, accessorMapping, luceneBuilderMapping, typeId, dictionaryService, classDef); + } + else if (cmisMapping.isValidCmisItem(classQName)) + { + typeId = cmisMapping.getCmisTypeId(BaseTypeId.CMIS_ITEM, classQName); + objectTypeDef = new ItemTypeDefinitionWrapper(cmisMapping, accessorMapping, luceneBuilderMapping, typeId, dictionaryService, classDef); + } + } + + return objectTypeDef; + } + + private Collection createTypeDefs(CMISDictionaryRegistry registry, + Collection classQNames) + { + Collection ret = new LinkedList<>(); + + for (QName classQName : classQNames) + { + AbstractTypeDefinitionWrapper objectTypeDef = createTypeDef(classQName); + if (objectTypeDef != null) + { + registry.registerTypeDefinition(objectTypeDef); + ret.add(objectTypeDef); + } + } + + return ret; + } + + private RelationshipTypeDefintionWrapper createAssocDef(QName classQName) + { + RelationshipTypeDefintionWrapper assocTypeDef = null; + + if(cmisMapping.isValidCmisRelationship(classQName)) + { + // create appropriate kind of type definition + AssociationDefinition assocDef = dictionaryService.getAssociation(classQName); + String typeId = cmisMapping.getCmisTypeId(BaseTypeId.CMIS_RELATIONSHIP, classQName); + assocTypeDef = new RelationshipTypeDefintionWrapper(cmisMapping, accessorMapping, luceneBuilderMapping, + typeId, dictionaryService, assocDef); + } + + return assocTypeDef; + } + + /** + * Create Relationship Definitions + * + * @param registry CMISDictionaryRegistry + * @param classQNames Collection createAssocDefs(CMISDictionaryRegistry registry, + Collection classQNames) + { + Collection ret = new LinkedList<>(); + + // register all other relationships + for (QName classQName : classQNames) + { + RelationshipTypeDefintionWrapper assocTypeDef = createAssocDef(classQName); + if(assocTypeDef != null) + { + registry.registerTypeDefinition(assocTypeDef); + ret.add(assocTypeDef); + } + } + + return ret; + } + + @Override + protected DictionaryInitializer getCoreDictionaryInitializer() + { + return coreDictionaryInitializer; + } + + @Override + protected DictionaryInitializer getTenantDictionaryInitializer() + { + return tenantDictionaryInitializer; + } +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/DocumentTypeDefinitionWrapper.java b/src/main/java/org/alfresco/opencmis/dictionary/DocumentTypeDefinitionWrapper.java new file mode 100644 index 0000000..0b334e3 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/DocumentTypeDefinitionWrapper.java @@ -0,0 +1,141 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.Collection; + +import org.alfresco.opencmis.CMISUtils; +import org.alfresco.opencmis.mapping.CMISMapping; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO9075; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.enums.ContentStreamAllowed; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.DocumentTypeDefinitionImpl; + +public class DocumentTypeDefinitionWrapper extends ShadowTypeDefinitionWrapper +{ + private static final long serialVersionUID = 1L; + + private DocumentTypeDefinitionImpl typeDef; + private DocumentTypeDefinitionImpl typeDefInclProperties; + private DictionaryService dictionaryService; + + public DocumentTypeDefinitionWrapper(CMISMapping cmisMapping, PropertyAccessorMapping accessorMapping, + PropertyLuceneBuilderMapping luceneBuilderMapping, String typeId, DictionaryService dictionaryService, ClassDefinition cmisClassDef) + { + this.dictionaryService = dictionaryService; + alfrescoName = cmisClassDef.getName(); + alfrescoClass = cmisMapping.getAlfrescoClass(alfrescoName); + + typeDef = new DocumentTypeDefinitionImpl(); + + typeDef.setBaseTypeId(BaseTypeId.CMIS_DOCUMENT); + typeDef.setId(typeId); + typeDef.setLocalName(alfrescoName.getLocalName()); + typeDef.setLocalNamespace(alfrescoName.getNamespaceURI()); + + if (BaseTypeId.CMIS_DOCUMENT.value().equals(typeId)) + { + typeDef.setQueryName(ISO9075.encodeSQL(typeId)); + typeDef.setParentTypeId(null); + } else + { + typeDef.setQueryName(ISO9075.encodeSQL(cmisMapping.buildPrefixEncodedString(alfrescoName))); + QName parentQName = cmisMapping.getCmisType(cmisClassDef.getParentName()); + if (cmisMapping.isValidCmisDocument(parentQName)) + { + typeDef.setParentTypeId(cmisMapping.getCmisTypeId(BaseTypeId.CMIS_DOCUMENT, parentQName)); + } + } + + typeDef.setDisplayName(null); + typeDef.setDescription(null); + + typeDef.setIsCreatable(true); + typeDef.setIsQueryable(true); + typeDef.setIsFulltextIndexed(true); + typeDef.setIsControllablePolicy(false); + typeDef.setIsControllableAcl(true); + typeDef.setIsIncludedInSupertypeQuery(cmisClassDef.getIncludedInSuperTypeQuery()); + typeDef.setIsFileable(true); + typeDef.setContentStreamAllowed(ContentStreamAllowed.ALLOWED); + typeDef.setIsVersionable(true); + + typeDefInclProperties = CMISUtils.copy(typeDef); + setTypeDefinition(typeDef, typeDefInclProperties); + + createOwningPropertyDefinitions(cmisMapping, accessorMapping, luceneBuilderMapping, dictionaryService, cmisClassDef); + createActionEvaluators(accessorMapping, BaseTypeId.CMIS_DOCUMENT); + } + + @Override + public void updateDefinition(DictionaryService dictionaryService) + { + TypeDefinition typeDef = dictionaryService.getType(alfrescoName); + + if (typeDef != null) + { + setTypeDefDisplayName(typeDef.getTitle(dictionaryService)); + setTypeDefDescription(typeDef.getDescription(dictionaryService)); + } + else + { + super.updateDefinition(dictionaryService); + } + + updateTypeDefInclProperties(); + } + + @Override + public PropertyDefinitionWrapper getPropertyById(String propertyId) + { + updateProperty(dictionaryService, propertiesById.get(propertyId)); + return propertiesById.get(propertyId); + } + + @Override + public Collection getProperties() + { + updateProperties(dictionaryService); + return propertiesById.values(); + } + + @Override + public Collection getProperties(boolean update) + { + if (update) + { + return getProperties(); + } + else + { + return propertiesById.values(); + } + } +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/FilteredDictionaryComponent.java b/src/main/java/org/alfresco/opencmis/dictionary/FilteredDictionaryComponent.java new file mode 100644 index 0000000..4224085 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/FilteredDictionaryComponent.java @@ -0,0 +1,93 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.Collection; + +import org.alfresco.repo.dictionary.DictionaryComponent; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.QName; + +/** + * A DictionaryComponent that uses a QNameFilter to constrain what is returned. + * + * @author Gethin James + */ +public class FilteredDictionaryComponent extends DictionaryComponent +{ + QNameFilter filter; + + @Override + public Collection getAllTypes() + { + return filter.filterQName(super.getAllTypes()); + } + + @Override + public Collection getSubTypes(QName superType, boolean follow) + { + return filter.filterQName(super.getSubTypes(superType, follow)); + } + + @Override + public Collection getAllAspects() + { + return filter.filterQName(super.getAllAspects()); + } + + @Override + public Collection getAllAssociations() + { + return filter.filterQName(super.getAllAssociations()); + } + + @Override + public Collection getSubAspects(QName superAspect, boolean follow) + { + return filter.filterQName(super.getSubAspects(superAspect, follow)); + } + + @Override + public TypeDefinition getType(QName name) + { + if (filter.isExcluded(name)) return null; //Don't return an excluded type + return super.getType(name); + } + + @Override + public AspectDefinition getAspect(QName name) + { + if (filter.isExcluded(name)) return null; //Don't return an excluded type + return super.getAspect(name); + } + + public void setFilter(QNameFilter filter) + { + this.filter = filter; + } + +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/FolderTypeDefintionWrapper.java b/src/main/java/org/alfresco/opencmis/dictionary/FolderTypeDefintionWrapper.java new file mode 100644 index 0000000..4b3dc9e --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/FolderTypeDefintionWrapper.java @@ -0,0 +1,147 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.Collection; + +import org.alfresco.model.ContentModel; +import org.alfresco.opencmis.CMISUtils; +import org.alfresco.opencmis.mapping.CMISMapping; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO9075; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.FolderTypeDefinitionImpl; + +public class FolderTypeDefintionWrapper extends ShadowTypeDefinitionWrapper +{ + private static final long serialVersionUID = 1L; + + private FolderTypeDefinitionImpl typeDef; + private FolderTypeDefinitionImpl typeDefInclProperties; + private DictionaryService dictionaryService; + + + public FolderTypeDefintionWrapper(CMISMapping cmisMapping, PropertyAccessorMapping accessorMapping, + PropertyLuceneBuilderMapping luceneBuilderMapping, String typeId, DictionaryService dictionaryService, ClassDefinition cmisClassDef) + { + this.dictionaryService = dictionaryService; + alfrescoName = cmisClassDef.getName(); + alfrescoClass = cmisMapping.getAlfrescoClass(alfrescoName); + + typeDef = new FolderTypeDefinitionImpl(); + + typeDef.setBaseTypeId(BaseTypeId.CMIS_FOLDER); + typeDef.setId(typeId); + typeDef.setLocalName(alfrescoName.getLocalName()); + typeDef.setLocalNamespace(alfrescoName.getNamespaceURI()); + + boolean isSystemFolder = false; + if (BaseTypeId.CMIS_FOLDER.value().equals(typeId)) + { + typeDef.setQueryName(ISO9075.encodeSQL(typeId)); + typeDef.setParentTypeId(null); + } else + { + typeDef.setQueryName(ISO9075.encodeSQL(cmisMapping.buildPrefixEncodedString(alfrescoName))); + QName parentQName = cmisMapping.getCmisType(cmisClassDef.getParentName()); + if (cmisMapping.isValidCmisFolder(parentQName)) + { + typeDef.setParentTypeId(cmisMapping.getCmisTypeId(BaseTypeId.CMIS_FOLDER, parentQName)); + } + + if (alfrescoName.equals(ContentModel.TYPE_SYSTEM_FOLDER) + || cmisMapping.getDictionaryService().isSubClass(alfrescoName, ContentModel.TYPE_SYSTEM_FOLDER)) + { + isSystemFolder = true; + } + } + + typeDef.setDisplayName(null); + typeDef.setDescription(null); + + typeDef.setIsCreatable(!isSystemFolder); + typeDef.setIsQueryable(true); + typeDef.setIsFulltextIndexed(true); + typeDef.setIsControllablePolicy(false); + typeDef.setIsControllableAcl(true); + typeDef.setIsIncludedInSupertypeQuery(cmisClassDef.getIncludedInSuperTypeQuery()); + typeDef.setIsFileable(true); + + typeDefInclProperties = CMISUtils.copy(typeDef); + setTypeDefinition(typeDef, typeDefInclProperties); + + createOwningPropertyDefinitions(cmisMapping, accessorMapping, luceneBuilderMapping, dictionaryService, cmisClassDef); + createActionEvaluators(accessorMapping, BaseTypeId.CMIS_FOLDER); + } + + @Override + public void updateDefinition(DictionaryService dictionaryService) + { + TypeDefinition typeDef = dictionaryService.getType(alfrescoName); + + if (typeDef != null) + { + setTypeDefDisplayName(typeDef.getTitle(dictionaryService)); + setTypeDefDescription(typeDef.getDescription(dictionaryService)); + } + else + { + super.updateDefinition(dictionaryService); + } + + updateTypeDefInclProperties(); + } + + @Override + public PropertyDefinitionWrapper getPropertyById(String propertyId) + { + updateProperty(dictionaryService, propertiesById.get(propertyId)); + return propertiesById.get(propertyId); + } + + @Override + public Collection getProperties() + { + updateProperties(dictionaryService); + return propertiesById.values(); + } + + @Override + public Collection getProperties(boolean update) + { + if (update) + { + return getProperties(); + } + else + { + return propertiesById.values(); + } + } +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/ItemTypeDefinitionWrapper.java b/src/main/java/org/alfresco/opencmis/dictionary/ItemTypeDefinitionWrapper.java new file mode 100644 index 0000000..ffa77fc --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/ItemTypeDefinitionWrapper.java @@ -0,0 +1,149 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.Collection; + +import org.alfresco.opencmis.CMISUtils; +import org.alfresco.opencmis.mapping.CMISMapping; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO9075; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.ItemTypeDefinitionImpl; + +public class ItemTypeDefinitionWrapper extends ShadowTypeDefinitionWrapper +{ + private static final long serialVersionUID = 1L; + + private ItemTypeDefinitionImpl typeDef; + private ItemTypeDefinitionImpl typeDefInclProperties; + private DictionaryService dictionaryService; + + public ItemTypeDefinitionWrapper(CMISMapping cmisMapping, PropertyAccessorMapping accessorMapping, + PropertyLuceneBuilderMapping luceneBuilderMapping, String typeId, DictionaryService dictionaryService, ClassDefinition cmisClassDef) + { + this.dictionaryService = dictionaryService; + alfrescoName = cmisClassDef.getName(); + alfrescoClass = cmisMapping.getAlfrescoClass(alfrescoName); + + typeDef = new ItemTypeDefinitionImpl(); + + typeDef.setBaseTypeId(BaseTypeId.CMIS_ITEM); + typeDef.setId(typeId); + typeDef.setLocalName(alfrescoName.getLocalName()); + typeDef.setLocalNamespace(alfrescoName.getNamespaceURI()); + + if (BaseTypeId.CMIS_ITEM.value().equals(typeId) ) + { + typeDef.setQueryName(ISO9075.encodeSQL(typeId)); + typeDef.setParentTypeId(null); + } + else + { + typeDef.setQueryName(ISO9075.encodeSQL(cmisMapping.buildPrefixEncodedString(alfrescoName))); + QName parentQName = cmisMapping.getCmisType(cmisClassDef.getParentName()); + if(parentQName != null) + { + typeDef.setParentTypeId(cmisMapping.getCmisTypeId(BaseTypeId.CMIS_ITEM, parentQName)); + } + } + + typeDef.setDisplayName(null); + typeDef.setDescription(null); + + if (BaseTypeId.CMIS_ITEM.value().equals(typeId) ) + { + typeDef.setIsCreatable(false); // cmis:item is abstract + // TEMP work around for select * from cmis:item which lists folders and files + typeDef.setIsQueryable(false); + } + else + { + typeDef.setIsCreatable(true); + typeDef.setIsQueryable(true); + } + + typeDef.setIsFulltextIndexed(true); + typeDef.setIsControllablePolicy(true); + typeDef.setIsControllableAcl(true); + typeDef.setIsIncludedInSupertypeQuery(cmisClassDef.getIncludedInSuperTypeQuery()); + typeDef.setIsFileable(false); + + typeDefInclProperties = CMISUtils.copy(typeDef); + setTypeDefinition(typeDef, typeDefInclProperties); + + createOwningPropertyDefinitions(cmisMapping, accessorMapping, luceneBuilderMapping, dictionaryService, cmisClassDef); + createActionEvaluators(accessorMapping, BaseTypeId.CMIS_ITEM); + } + + @Override + public void updateDefinition(DictionaryService dictionaryService) + { + TypeDefinition typeDef = dictionaryService.getType(alfrescoName); + + if (typeDef != null) + { + setTypeDefDisplayName(typeDef.getTitle(dictionaryService)); + setTypeDefDescription(typeDef.getDescription(dictionaryService)); + } + else + { + super.updateDefinition(dictionaryService); + } + + updateTypeDefInclProperties(); + } + + @Override + public PropertyDefinitionWrapper getPropertyById(String propertyId) + { + updateProperty(dictionaryService, propertiesById.get(propertyId)); + return propertiesById.get(propertyId); + } + + @Override + public Collection getProperties() + { + updateProperties(dictionaryService); + return propertiesById.values(); + } + + @Override + public Collection getProperties(boolean update) + { + if (update) + { + return getProperties(); + } + else + { + return propertiesById.values(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/opencmis/dictionary/PolicyTypeDefintionWrapper.java b/src/main/java/org/alfresco/opencmis/dictionary/PolicyTypeDefintionWrapper.java new file mode 100644 index 0000000..a524302 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/PolicyTypeDefintionWrapper.java @@ -0,0 +1,277 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.opencmis.CMISUtils; +import org.alfresco.opencmis.mapping.CMISMapping; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO9075; +import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.PolicyTypeDefinitionImpl; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class PolicyTypeDefintionWrapper extends AbstractTypeDefinitionWrapper +{ + private static final long serialVersionUID = 1L; + // Logger + protected static final Log logger = LogFactory.getLog(PolicyTypeDefintionWrapper.class); + + private PolicyTypeDefinitionImpl typeDef; + private PolicyTypeDefinitionImpl typeDefInclProperties; + private DictionaryService dictionaryService; + + public PolicyTypeDefintionWrapper(CMISMapping cmisMapping, PropertyAccessorMapping propertyAccessorMapping, + PropertyLuceneBuilderMapping luceneBuilderMapping, String typeId, DictionaryService dictionaryService, ClassDefinition cmisClassDef) + { + this.dictionaryService = dictionaryService; + alfrescoName = cmisClassDef.getName(); + alfrescoClass = cmisMapping.getAlfrescoClass(alfrescoName); + + typeDef = new PolicyTypeDefinitionImpl(); + + typeDef.setBaseTypeId(BaseTypeId.CMIS_POLICY); + typeDef.setId(typeId); + typeDef.setLocalName(alfrescoName.getLocalName()); + typeDef.setLocalNamespace(alfrescoName.getNamespaceURI()); + + if (BaseTypeId.CMIS_POLICY.value().equals(typeId)) + { + typeDef.setQueryName(ISO9075.encodeSQL(typeId)); + typeDef.setParentTypeId(null); + } else + { + typeDef.setQueryName(ISO9075.encodeSQL(cmisMapping.buildPrefixEncodedString(alfrescoName))); + QName parentQName = cmisMapping.getCmisType(cmisClassDef.getParentName()); + if (parentQName == null) + { + typeDef.setParentTypeId(cmisMapping.getCmisTypeId(CMISMapping.ASPECTS_QNAME)); + } else if (cmisMapping.isValidCmisPolicy(parentQName)) + { + typeDef.setParentTypeId(cmisMapping.getCmisTypeId(BaseTypeId.CMIS_POLICY, parentQName)); + } else + { + throw new IllegalStateException("The CMIS type model should ignore aspects that inherit from excluded aspects"); + } + } + + typeDef.setDisplayName(null); + typeDef.setDescription(null); + + typeDef.setIsCreatable(false); + typeDef.setIsQueryable(true); + typeDef.setIsFulltextIndexed(true); + typeDef.setIsControllablePolicy(false); + typeDef.setIsControllableAcl(false); + typeDef.setIsIncludedInSupertypeQuery(cmisClassDef.getIncludedInSuperTypeQuery()); + typeDef.setIsFileable(false); + + typeDefInclProperties = CMISUtils.copy(typeDef); + setTypeDefinition(typeDef, typeDefInclProperties); + + createOwningPropertyDefinitions(cmisMapping, propertyAccessorMapping, luceneBuilderMapping, dictionaryService, cmisClassDef); + createActionEvaluators(propertyAccessorMapping, BaseTypeId.CMIS_POLICY); + } + + @Override + public List connectParentAndSubTypes(CMISMapping cmisMapping, CMISDictionaryRegistry registry, + DictionaryService dictionaryService) + { + String parentTypeId = typeDef.getParentTypeId(); + + // find parent + if (typeDef.getParentTypeId() != null) + { + parent = registry.getTypeDefByTypeId(parentTypeId); +// if(registry.getTenant() != null && parent != null && registry.getTypeDefByTypeId(parentTypeId, false) == null) +// { +// // this is a tenant registry and the parent is not defined locally so add this type as a child of it +// registry.addChild(parent.getTypeId(), this); +// } + } + else + { + if (!isBaseType()) + { + throw new AlfrescoRuntimeException("Type " + typeDef.getId() + " has no parent!"); + } + + parent = null; + } + + // find children + Collection childrenNames = null; + + if (isBaseType()) + { + // add the "Aspects" type to the CMIS Policy type + childrenNames = new ArrayList(); + childrenNames.add(CMISMapping.ASPECTS_QNAME); + } + else if (getAlfrescoName().equals(CMISMapping.ASPECTS_QNAME)) + { + // add all root aspects to the "Aspects" type + childrenNames = new ArrayList(); + + String aspectsTypeId = cmisMapping.getCmisTypeId(CMISMapping.ASPECTS_QNAME); + for (AbstractTypeDefinitionWrapper tdw : registry.getTypeDefs(false)) + { +// TypeDefinitionWrapper parent = tdw.getParent(); +// if(tdw.getTenantId().equals(parent.getTenantId())) +// { + // type and parent in same tenant +// } + String parentId = tdw.getTypeDefinition(false).getParentTypeId(); + if ((parentId != null) && parentId.equals(aspectsTypeId)) + { + childrenNames.add(tdw.getAlfrescoName()); + } + } + } + else + { + // add all non-root aspects in this tenant to their parent + childrenNames = dictionaryService.getSubAspects(cmisMapping.getAlfrescoClass(getAlfrescoName()), false); + } + + List children = new LinkedList(); + for (QName childName : childrenNames) + { + if (cmisMapping.isValidCmisPolicy(childName)) + { + TypeDefinitionWrapper child = registry.getTypeDefByQName(childName); + + if (child == null) + { + throw new AlfrescoRuntimeException("Failed to retrieve sub type for type id " + childName + + " for parent type " + getAlfrescoName() + "!"); + } + children.add(child); + } + else + { + logger.info("Not a policy: " + childName); + } + } + + return children; +// registry.setChildren(typeDef.getId(), children); + } + + public void resolveInheritance(CMISMapping cmisMapping, + CMISDictionaryRegistry registry, DictionaryService dictionaryService) + { + PropertyDefinition propertyDefintion; + + if (parent != null) + { + for (PropertyDefinitionWrapper propDef : parent.getProperties(false)) + { + if (propertiesById.containsKey(propDef.getPropertyId())) + { + continue; + } + + org.alfresco.service.cmr.dictionary.PropertyDefinition alfrescoPropDef = dictionaryService.getProperty( + propDef.getOwningType().getAlfrescoName(), propDef.getAlfrescoName()); + + propertyDefintion = createPropertyDefinition(cmisMapping, propDef.getPropertyId(), + alfrescoPropDef.getName(), dictionaryService, alfrescoPropDef, true); + + if (propertyDefintion != null) + { + registerProperty(new BasePropertyDefintionWrapper(propertyDefintion, alfrescoPropDef.getName(), + propDef.getOwningType(), propDef.getPropertyAccessor(), propDef.getPropertyLuceneBuilder())); + } + } + } + + List children = registry.getChildren(typeDef.getId()); + for (TypeDefinitionWrapper child : children) + { + if (child instanceof AbstractTypeDefinitionWrapper) + { + ((AbstractTypeDefinitionWrapper) child).resolveInheritance(cmisMapping, registry, + dictionaryService); + } + } + } + + @Override + public void updateDefinition(DictionaryService dictionaryService) + { + AspectDefinition aspectDef = dictionaryService.getAspect(alfrescoName); + + if (aspectDef != null) + { + setTypeDefDisplayName(aspectDef.getTitle(dictionaryService)); + setTypeDefDescription(aspectDef.getDescription(dictionaryService)); + } + else + { + super.updateDefinition(dictionaryService); + } + + updateTypeDefInclProperties(); + } + + @Override + public PropertyDefinitionWrapper getPropertyById(String propertyId) + { + updateProperty(dictionaryService, propertiesById.get(propertyId)); + return propertiesById.get(propertyId); + } + + @Override + public Collection getProperties() + { + updateProperties(dictionaryService); + return propertiesById.values(); + } + + @Override + public Collection getProperties(boolean update) + { + if (update) + { + return getProperties(); + } + else + { + return propertiesById.values(); + } + } +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/PropertyAccessorMapping.java b/src/main/java/org/alfresco/opencmis/dictionary/PropertyAccessorMapping.java new file mode 100644 index 0000000..2b44d69 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/PropertyAccessorMapping.java @@ -0,0 +1,65 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.Map; + +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.opencmis.commons.enums.Action; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; + + +/** + * Encapsulate the mapping of property to property accessor + * + * @author davidc + */ +public interface PropertyAccessorMapping +{ + /** + * Gets a property accessor + * + * @param propertyId property id + * @return property accessor + */ + public CMISPropertyAccessor getPropertyAccessor(String propertyId); + + /** + * Create a direct node property accessor + * + * @param propertyId property id + * @param propertyName node property name + * @return property accessor + */ + public CMISPropertyAccessor createDirectPropertyAccessor(String propertyId, QName propertyName); + + /** + * Gets the Action Evaluators applicable for the given CMIS Scope + * + * @param scope BaseTypeId + */ + public Map getActionEvaluators(BaseTypeId scope); +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/PropertyDefinitionWrapper.java b/src/main/java/org/alfresco/opencmis/dictionary/PropertyDefinitionWrapper.java new file mode 100644 index 0000000..898dfaa --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/PropertyDefinitionWrapper.java @@ -0,0 +1,44 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition; + +public interface PropertyDefinitionWrapper +{ + PropertyDefinition getPropertyDefinition(); + + String getPropertyId(); + + QName getAlfrescoName(); + + TypeDefinitionWrapper getOwningType(); + + CMISPropertyAccessor getPropertyAccessor(); + + CMISPropertyLuceneBuilder getPropertyLuceneBuilder(); +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/PropertyLuceneBuilderMapping.java b/src/main/java/org/alfresco/opencmis/dictionary/PropertyLuceneBuilderMapping.java new file mode 100644 index 0000000..44307c1 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/PropertyLuceneBuilderMapping.java @@ -0,0 +1,52 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import org.alfresco.service.namespace.QName; + +/** + * Encapsulate the mapping of property to lucene builder + * + * @author davidc + */ +public interface PropertyLuceneBuilderMapping +{ + /** + * Gets a property lucene builder + * + * @param propertyId property id + * @return property builder + */ + public CMISPropertyLuceneBuilder getPropertyLuceneBuilder(String propertyId); + + /** + * Create a direct node property lucene builder + * + * @param propertyName node property name + * @return property lucene builder + */ + public CMISPropertyLuceneBuilder createDirectPropertyLuceneBuilder(QName propertyName); +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/QNameFilter.java b/src/main/java/org/alfresco/opencmis/dictionary/QNameFilter.java new file mode 100644 index 0000000..380a12e --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/QNameFilter.java @@ -0,0 +1,67 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.Collection; + +import org.alfresco.service.namespace.QName; + +/** + * Filters QNames and excludes any + * that are in a predefined list. + * + * @author steveglover + * @author Gethin James + */ +public interface QNameFilter +{ + public static final String WILDCARD = "*"; + + /** + * Filters out any QName defined in the "excludedTypes" property + * + * @param typesToFilter - original list + * @return the filtered list + */ + Collection filterQName(Collection typesToFilter); + + /** + * Indicates that this QName should be excluded. + * @param typeQName QName + * @return boolean true if it is excluded + */ + boolean isExcluded(QName typeQName); + + /** + * Exclude the type + * + * @param typeQName the type to exclude + * + */ + void setExcluded(QName typeQName, boolean excluded); + + void initFilter(); +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/QNameFilterImpl.java b/src/main/java/org/alfresco/opencmis/dictionary/QNameFilterImpl.java new file mode 100644 index 0000000..965f7f9 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/QNameFilterImpl.java @@ -0,0 +1,192 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.namespace.QName; + +/** + * Filters a QName and excludes any + * that are defined using the excludedTypes parameter. + * The list of types can either be defined using a property name such as "cm:name" or with a wildcard: "cm:*" + * It validates the definitions against the DataDictionary. + * + * @author steveglover + * @author Gethin James + */ +public class QNameFilterImpl implements QNameFilter +{ + private Map excludedQNames; + private Set excludedModels; + + private List excludedTypes; + + /** + * Filters out any QName defined in the "excludedModels" property + * + * @param typesToFilter - original list + * @return the filtered list + */ + public Collection filterQName(Collection typesToFilter) + { + Collection filteredTypes = new ArrayList(); + if (!excludedQNames.isEmpty() || !excludedModels.isEmpty()) + { + //If we have a exclusion list then loop through and exclude models /types + //that are in this list. + for (QName classQName : typesToFilter) + { + if(!isExcluded(classQName)) +// if (!excludedQNames.contains(classQName) && !excludedModels.contains(classQName.getNamespaceURI())) + { + //Not excluded so add it + filteredTypes.add(classQName); + } + } + } + else + { + filteredTypes = typesToFilter; + } + return filteredTypes; + } + + + /** + * Processes the user-defined list of types into valid QNames and models, it validates them + * against the dictionary and also supports wildcards + */ + protected void preprocessExcludedTypes(List excludeTypeNames) + { + if (excludeTypeNames == null || excludeTypeNames.isEmpty()) return; + + Map qNamesToExclude = new HashMap(); + Set modelsToExclude = new HashSet(); + + for (String typeDefinition : excludeTypeNames) + { + final QName typeDef = QName.createQName(typeDefinition); + if (WILDCARD.equals(typeDef.getLocalName())) + { + modelsToExclude.add(typeDef.getNamespaceURI()); + } + else + { + qNamesToExclude.put(typeDef, Boolean.TRUE); // valid so add it to the list + } + } + + this.excludedModels = modelsToExclude; + this.excludedQNames = qNamesToExclude; + } + + /** + * Indicates that this QName should be excluded. + * @param typeQName QName + * @return boolean true if it is excluded + */ + public boolean isExcluded(QName typeQName) + { + Boolean isExcluded = excludedQNames.get(typeQName); + return (isExcluded != null && isExcluded.booleanValue() || excludedModels.contains(typeQName.getNamespaceURI())); + } + + @Override + public void initFilter() + { + if (excludedTypes == null || excludedTypes.isEmpty()) + { + excludedTypes = listOfHardCodedExcludedTypes(); + } + preprocessExcludedTypes(excludedTypes); + } + + public void setExcludedTypes(List excludedTypes) + { + this.excludedTypes = excludedTypes; + } + + /** + * I don't like hard code values, but have been persuaded its less pain + * than having to keep a config file in sync with both sides of SOLR. + * + */ + public static List listOfHardCodedExcludedTypes() { + List hardCodeListOfTypes = new ArrayList(); + + hardCodeListOfTypes.add("{http://www.alfresco.org/model/imap/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/publishing/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/publishingworkflow/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/publishing/twitter/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/publishing/slideshare/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/publishing/facebook/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/publishing/youtube/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/publishing/linkedin/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/publishing/flickr/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/transfer/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/emailserver/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/calendar}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/blogintegration/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/linksmodel/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/datalist/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/forum/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/cloud/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/bpm/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/workflow/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/workflow/invite/moderated/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/workflow/invite/nominated/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/workflow/cloud/resetpassword/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/workflow/cloud/siteinvitation/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/workflow/signup/selfsignup/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/versionstore/2.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/versionstore/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/action/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/application/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/rule/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/rendition/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/qshare/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/sync/1.0}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/content/1.0}thumbnailed"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/content/1.0}failedThumbnailSource"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/cmis/custom}*"); + hardCodeListOfTypes.add("{http://www.alfresco.org/model/hybridworkflow/1.0}*"); + return hardCodeListOfTypes; + } + + @Override + public void setExcluded(QName typeQName, boolean excluded) + { + excludedQNames.put(typeQName, Boolean.valueOf(excluded)); + } + +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/RelationshipTypeDefintionWrapper.java b/src/main/java/org/alfresco/opencmis/dictionary/RelationshipTypeDefintionWrapper.java new file mode 100644 index 0000000..affd9ff --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/RelationshipTypeDefintionWrapper.java @@ -0,0 +1,272 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.opencmis.CMISUtils; +import org.alfresco.opencmis.mapping.CMISMapping; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.util.ISO9075; +import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.RelationshipTypeDefinitionImpl; + +public class RelationshipTypeDefintionWrapper extends AbstractTypeDefinitionWrapper +{ + private static final long serialVersionUID = 1L; + + private RelationshipTypeDefinitionImpl typeDef; + private RelationshipTypeDefinitionImpl typeDefInclProperties; + private DictionaryService dictionaryService; + + public RelationshipTypeDefintionWrapper(CMISMapping cmisMapping, PropertyAccessorMapping accessorMapping, + PropertyLuceneBuilderMapping luceneBuilderMapping, String typeId, DictionaryService dictionaryService, ClassDefinition cmisClassDef) + { + this.dictionaryService = dictionaryService; + alfrescoName = cmisClassDef.getName(); + alfrescoClass = cmisMapping.getAlfrescoClass(alfrescoName); + + typeDef = new RelationshipTypeDefinitionImpl(); + + typeDef.setBaseTypeId(BaseTypeId.CMIS_RELATIONSHIP); + typeDef.setId(typeId); + typeDef.setLocalName(alfrescoName.getLocalName()); + typeDef.setLocalNamespace(alfrescoName.getNamespaceURI()); + + if (BaseTypeId.CMIS_RELATIONSHIP.value().equals(typeId)) + { + typeDef.setQueryName(ISO9075.encodeSQL(typeId)); + typeDef.setParentTypeId(null); + typeDef.setIsCreatable(false); + } else + { + typeDef.setQueryName(ISO9075.encodeSQL(cmisMapping.buildPrefixEncodedString(alfrescoName))); + typeDef.setParentTypeId(BaseTypeId.CMIS_RELATIONSHIP.value()); + typeDef.setIsCreatable(true); + } + + typeDef.setDisplayName(null); + typeDef.setDescription(null); + + typeDef.setIsQueryable(false); + typeDef.setIsFulltextIndexed(false); + typeDef.setIsControllablePolicy(false); + typeDef.setIsControllableAcl(false); + typeDef.setIsIncludedInSupertypeQuery(true); + typeDef.setIsFileable(false); + + typeDefInclProperties = CMISUtils.copy(typeDef); + setTypeDefinition(typeDef, typeDefInclProperties); + + createOwningPropertyDefinitions(cmisMapping, accessorMapping, luceneBuilderMapping, dictionaryService, cmisClassDef); + createActionEvaluators(accessorMapping, BaseTypeId.CMIS_RELATIONSHIP); + } + + public RelationshipTypeDefintionWrapper(CMISMapping cmisMapping, PropertyAccessorMapping accessorMapping, + PropertyLuceneBuilderMapping luceneBuilderMapping, String typeId, DictionaryService dictionaryService, AssociationDefinition cmisAssocDef) + { + this.dictionaryService = dictionaryService; + alfrescoName = cmisAssocDef.getName(); + alfrescoClass = cmisMapping.getAlfrescoClass(alfrescoName); + + typeDef = new RelationshipTypeDefinitionImpl(); + + typeDef.setBaseTypeId(BaseTypeId.CMIS_RELATIONSHIP); + typeDef.setId(typeId); + typeDef.setLocalName(alfrescoName.getLocalName()); + typeDef.setLocalNamespace(alfrescoName.getNamespaceURI()); + + typeDef.setQueryName(cmisMapping.buildPrefixEncodedString(alfrescoName)); + typeDef.setParentTypeId(BaseTypeId.CMIS_RELATIONSHIP.value()); + + typeDef.setDisplayName(null); + typeDef.setDescription(null); + + typeDef.setIsCreatable(true); + typeDef.setIsQueryable(false); + typeDef.setIsFulltextIndexed(false); + typeDef.setIsControllablePolicy(false); + typeDef.setIsControllableAcl(false); + typeDef.setIsIncludedInSupertypeQuery(true); + typeDef.setIsFileable(false); + + ArrayList both = new ArrayList(2); + both.add(BaseTypeId.CMIS_DOCUMENT.value()); + both.add(BaseTypeId.CMIS_FOLDER.value()); + + String sourceTypeId = cmisMapping.getCmisTypeId(cmisMapping + .getCmisType(cmisAssocDef.getSourceClass().getName())); + if (sourceTypeId != null) + { + typeDef.setAllowedSourceTypes(Collections.singletonList(sourceTypeId)); + } + else + { + typeDef.setAllowedSourceTypes(both); + } + + String targetTypeId = cmisMapping.getCmisTypeId(cmisMapping + .getCmisType(cmisAssocDef.getTargetClass().getName())); + if (targetTypeId != null) + { + typeDef.setAllowedTargetTypes(Collections.singletonList(targetTypeId)); + } + else + { + typeDef.setAllowedTargetTypes(both); + } + + typeDefInclProperties = CMISUtils.copy(typeDef); + setTypeDefinition(typeDef, typeDefInclProperties); + createActionEvaluators(accessorMapping, BaseTypeId.CMIS_RELATIONSHIP); + } + + @Override + public List connectParentAndSubTypes(CMISMapping cmisMapping, CMISDictionaryRegistry registry, + DictionaryService dictionaryService) + { + String parentTypeId = typeDef.getParentTypeId(); + + // find parent + if (parentTypeId != null) + { + parent = registry.getTypeDefByTypeId(parentTypeId); + if(registry.getTenant() != null && parent != null && registry.getTypeDefByTypeId(parentTypeId, false) == null) + { + // this is a tenant registry and the parent is not defined locally so add this type as a child of it + registry.addChild(parent.getTypeId(), this); + } + } + else + { + if (!isBaseType()) + { + throw new AlfrescoRuntimeException("Type " + typeDef.getId() + " has no parent!"); + } + + parent = null; + } + + // find children + List children = new LinkedList(); + if (isBaseType()) + { + for (TypeDefinitionWrapper child : registry.getAssocDefs()) + { + children.add(child); + } + } + + return children; +// registry.setChildren(typeDef.getId(), children); + } + + public void resolveInheritance(CMISMapping cmisMapping, CMISDictionaryRegistry registry, + DictionaryService dictionaryService) + { + PropertyDefinition propertyDefintion; + + if (parent != null) + { + for (PropertyDefinitionWrapper propDef : parent.getProperties(false)) + { + org.alfresco.service.cmr.dictionary.PropertyDefinition alfrescoPropDef = dictionaryService.getProperty( + propDef.getOwningType().getAlfrescoName(), propDef.getAlfrescoName()); + + propertyDefintion = createPropertyDefinition(cmisMapping, propDef.getPropertyId(), + alfrescoPropDef.getName(), dictionaryService, alfrescoPropDef, true); + + if (propertyDefintion != null) + { + registerProperty(new BasePropertyDefintionWrapper(propertyDefintion, alfrescoPropDef.getName(), + propDef.getOwningType(), propDef.getPropertyAccessor(), propDef.getPropertyLuceneBuilder())); + } + } + } + + List children = registry.getChildren(typeDef.getId()); + for (TypeDefinitionWrapper child : children) + { + if (child instanceof AbstractTypeDefinitionWrapper) + { + ((AbstractTypeDefinitionWrapper) child).resolveInheritance(cmisMapping, registry, dictionaryService); + } + } + } + + @Override + public void updateDefinition(DictionaryService dictionaryService) + { + AssociationDefinition assocDef = dictionaryService.getAssociation(alfrescoName); + + if (assocDef != null) + { + setTypeDefDisplayName(assocDef.getTitle(dictionaryService)); + setTypeDefDescription(assocDef.getDescription(dictionaryService)); + } + else + { + super.updateDefinition(dictionaryService); + } + + updateTypeDefInclProperties(); + } + + @Override + public PropertyDefinitionWrapper getPropertyById(String propertyId) + { + updateProperty(dictionaryService, propertiesById.get(propertyId)); + return propertiesById.get(propertyId); + } + + @Override + public Collection getProperties() + { + updateProperties(dictionaryService); + return propertiesById.values(); + } + + @Override + public Collection getProperties(boolean update) + { + if (update) + { + return getProperties(); + } + else + { + return propertiesById.values(); + } + } +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/SecondaryTypeDefinitionWrapper.java b/src/main/java/org/alfresco/opencmis/dictionary/SecondaryTypeDefinitionWrapper.java new file mode 100644 index 0000000..8213d9a --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/SecondaryTypeDefinitionWrapper.java @@ -0,0 +1,272 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.opencmis.CMISUtils; +import org.alfresco.opencmis.mapping.CMISMapping; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO9075; +import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.SecondaryTypeDefinitionImpl; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class SecondaryTypeDefinitionWrapper extends AbstractTypeDefinitionWrapper +{ + private static final long serialVersionUID = 1L; + // Logger + protected static final Log logger = LogFactory.getLog(SecondaryTypeDefinitionWrapper.class); + + private SecondaryTypeDefinitionImpl typeDef; + private SecondaryTypeDefinitionImpl typeDefInclProperties; + private DictionaryService dictionaryService; + + public SecondaryTypeDefinitionWrapper(CMISMapping cmisMapping, PropertyAccessorMapping propertyAccessorMapping, + PropertyLuceneBuilderMapping luceneBuilderMapping, String typeId, DictionaryService dictionaryService, ClassDefinition cmisClassDef) + { + this.dictionaryService = dictionaryService; + alfrescoName = cmisClassDef.getName(); + alfrescoClass = cmisMapping.getAlfrescoClass(alfrescoName); + + typeDef = new SecondaryTypeDefinitionImpl(); + + typeDef.setBaseTypeId(BaseTypeId.CMIS_SECONDARY); + typeDef.setId(typeId); + typeDef.setLocalName(alfrescoName.getLocalName()); + typeDef.setLocalNamespace(alfrescoName.getNamespaceURI()); + + if (BaseTypeId.CMIS_SECONDARY.value().equals(typeId)) + { + typeDef.setQueryName(ISO9075.encodeSQL(typeId)); + typeDef.setParentTypeId(null); + } + else + { + typeDef.setQueryName(ISO9075.encodeSQL(cmisMapping.buildPrefixEncodedString(alfrescoName))); + QName parentQName = cmisMapping.getCmisType(cmisClassDef.getParentName()); + if (parentQName == null) + { + typeDef.setParentTypeId(cmisMapping.getCmisTypeId(CMISMapping.SECONDARY_TYPES_QNAME)); + } else if (cmisMapping.isValidCmisSecondaryType(parentQName)) + { + typeDef.setParentTypeId(cmisMapping.getCmisTypeId(BaseTypeId.CMIS_SECONDARY, parentQName)); + } else + { + throw new IllegalStateException("The CMIS type model should ignore aspects that inherit from excluded aspects"); + } + } + + typeDef.setDisplayName(null); + typeDef.setDescription(null); + + typeDef.setIsCreatable(false); + typeDef.setIsQueryable(true); + typeDef.setIsFulltextIndexed(true); + typeDef.setIsControllablePolicy(false); + typeDef.setIsControllableAcl(false); + typeDef.setIsIncludedInSupertypeQuery(cmisClassDef.getIncludedInSuperTypeQuery()); + typeDef.setIsFileable(false); + + typeDefInclProperties = CMISUtils.copy(typeDef); + setTypeDefinition(typeDef, typeDefInclProperties); + + createOwningPropertyDefinitions(cmisMapping, propertyAccessorMapping, luceneBuilderMapping, dictionaryService, cmisClassDef); + createActionEvaluators(propertyAccessorMapping, BaseTypeId.CMIS_SECONDARY); + } + + @Override + public List connectParentAndSubTypes(CMISMapping cmisMapping, CMISDictionaryRegistry registry, + DictionaryService dictionaryService) + { + String parentTypeId = typeDef.getParentTypeId(); + + // find parent + if (parentTypeId != null) + { + parent = registry.getTypeDefByTypeId(parentTypeId); + if(registry.getTenant() != null && parent != null && registry.getTypeDefByTypeId(parentTypeId, false) == null) + { + // this is a tenant registry and the parent is not defined locally so add this type as a child of it + registry.addChild(parent.getTypeId(), this); + } + } + else + { + if (!isBaseType()) + { + throw new AlfrescoRuntimeException("Type " + typeDef.getId() + " has no parent!"); + } + + parent = null; + } + + // find children +// children = new ArrayList(); + Collection childrenNames = null; + + if (isBaseType()) + { +// // add the "Aspects" type to the CMIS secondary type +// childrenNames = new ArrayList(); +// childrenNames.add(CMISMapping.SECONDARY_TYPES_QNAME); +// } else if (getAlfrescoName().equals(CMISMapping.SECONDARY_TYPES_QNAME)) +// { + // add all root aspects to the "Aspects" type + childrenNames = new ArrayList(); + + String aspectsTypeId = cmisMapping.getCmisTypeId(CMISMapping.SECONDARY_TYPES_QNAME); + for (AbstractTypeDefinitionWrapper tdw : registry.getTypeDefs()) + { + String parentId = tdw.getTypeDefinition(false).getParentTypeId(); + if ((parentId != null) && parentId.equals(aspectsTypeId)) + { + childrenNames.add(tdw.getAlfrescoName()); + } + } + } else + { + // add all non-root aspects to their parent + childrenNames = dictionaryService.getSubAspects(cmisMapping.getAlfrescoClass(getAlfrescoName()), false); + } + + List children = new LinkedList(); + for (QName childName : childrenNames) + { + if (cmisMapping.isValidCmisSecondaryType(childName)) + { + TypeDefinitionWrapper child = registry.getTypeDefByQName(childName); + + if (child == null) + { + throw new AlfrescoRuntimeException("Failed to retrieve sub type for type id " + childName + + " for parent type " + getAlfrescoName() + "!"); + } + children.add(child); + } + else + { + logger.info("Not a secondary type: " + childName); + } + } + + return children; +// registry.setChildren(typeDef.getId(), children); + } + + public void resolveInheritance(CMISMapping cmisMapping, + CMISDictionaryRegistry registry, DictionaryService dictionaryService) + { + PropertyDefinition propertyDefintion; + + if (parent != null) + { + for (PropertyDefinitionWrapper propDef : parent.getProperties(false)) + { + if (propertiesById.containsKey(propDef.getPropertyId())) + { + continue; + } + + org.alfresco.service.cmr.dictionary.PropertyDefinition alfrescoPropDef = dictionaryService.getProperty( + propDef.getOwningType().getAlfrescoName(), propDef.getAlfrescoName()); + + propertyDefintion = createPropertyDefinition(cmisMapping, propDef.getPropertyId(), + alfrescoPropDef.getName(), dictionaryService, alfrescoPropDef, true); + + if (propertyDefintion != null) + { + registerProperty(new BasePropertyDefintionWrapper(propertyDefintion, alfrescoPropDef.getName(), + propDef.getOwningType(), propDef.getPropertyAccessor(), propDef.getPropertyLuceneBuilder())); + } + } + } + + List children = registry.getChildren(typeDef.getId()); + for (TypeDefinitionWrapper child : children) + { + if (child instanceof AbstractTypeDefinitionWrapper) + { + ((AbstractTypeDefinitionWrapper) child).resolveInheritance(cmisMapping, registry, + dictionaryService); + } + } + } + + @Override + public void updateDefinition(DictionaryService dictionaryService) + { + AspectDefinition aspectDef = dictionaryService.getAspect(alfrescoName); + + if (aspectDef != null) + { + setTypeDefDisplayName(aspectDef.getTitle(dictionaryService)); + setTypeDefDescription(aspectDef.getDescription(dictionaryService)); + } + else + { + super.updateDefinition(dictionaryService); + } + + updateTypeDefInclProperties(); + } + + @Override + public PropertyDefinitionWrapper getPropertyById(String propertyId) + { + updateProperty(dictionaryService, propertiesById.get(propertyId)); + return propertiesById.get(propertyId); + } + + @Override + public Collection getProperties() + { + updateProperties(dictionaryService); + return propertiesById.values(); + } + + @Override + public Collection getProperties(boolean update) + { + if (update) + { + return getProperties(); + } + else + { + return propertiesById.values(); + } + } +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/ShadowTypeDefinitionWrapper.java b/src/main/java/org/alfresco/opencmis/dictionary/ShadowTypeDefinitionWrapper.java new file mode 100644 index 0000000..29e6284 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/ShadowTypeDefinitionWrapper.java @@ -0,0 +1,137 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.opencmis.mapping.CMISMapping; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition; + +public abstract class ShadowTypeDefinitionWrapper extends AbstractTypeDefinitionWrapper +{ + + private static final long serialVersionUID = 1L; + + @Override + public List connectParentAndSubTypes(CMISMapping cmisMapping, CMISDictionaryRegistry registry, + DictionaryService dictionaryService) + { + String parentTypeId = typeDef.getParentTypeId(); + + // find parent + if (parentTypeId != null) + { + parent = registry.getTypeDefByTypeId(parentTypeId); + if(registry.getTenant() != null && parent != null && registry.getTypeDefByTypeId(parentTypeId, false) == null) + { + // this is a tenant registry and the parent is not defined locally so add this type as a child of it + registry.addChild(parent.getTypeId(), this); + } + } + else + { + if (!isBaseType()) + { + throw new AlfrescoRuntimeException("Type " + typeDef.getId() + " has no parent!"); + } + + parent = null; + } + +// if(parent != null) +// { +// List children = new LinkedList(); +// children.add(this); +// registry.setChildren(parent.getTypeId(), children); +// } + + // find children +// children = new ArrayList(); + List children = new LinkedList(); + Collection childrenNames = dictionaryService.getSubTypes(cmisMapping.getAlfrescoClass(getAlfrescoName()), + false); + for (QName childName : childrenNames) + { + if (cmisMapping.isValidCmisObject(getBaseTypeId(), childName)) + { + TypeDefinitionWrapper child = registry.getTypeDefByQName(childName); + + if (child == null) + { + throw new AlfrescoRuntimeException("Failed to retrieve sub type for type id " + childName + + " for parent type " + getAlfrescoName() + "!"); + } + children.add(child); + } + } + return children; +// registry.setChildren(typeDef.getId(), children); + } + + public void resolveInheritance(CMISMapping cmisMapping, + CMISDictionaryRegistry registry, DictionaryService dictionaryService) + { + PropertyDefinition propertyDefintion; + + if (parent != null) + { + for (PropertyDefinitionWrapper propDef : parent.getProperties(false)) + { + if (propertiesById.containsKey(propDef.getPropertyId())) + { + continue; + } + + org.alfresco.service.cmr.dictionary.PropertyDefinition alfrescoPropDef = dictionaryService.getProperty( + propDef.getOwningType().getAlfrescoName(), propDef.getAlfrescoName()); + + propertyDefintion = createPropertyDefinition(cmisMapping, propDef.getPropertyId(), + alfrescoPropDef.getName(), dictionaryService, alfrescoPropDef, true); + + if (propertyDefintion != null) + { + registerProperty(new BasePropertyDefintionWrapper(propertyDefintion, alfrescoPropDef.getName(), + propDef.getOwningType(), propDef.getPropertyAccessor(), propDef.getPropertyLuceneBuilder())); + } + } + } + + List children = registry.getChildren(typeDef.getId()); + for (TypeDefinitionWrapper child : children) + { + if (child instanceof AbstractTypeDefinitionWrapper) + { + ((AbstractTypeDefinitionWrapper) child).resolveInheritance(cmisMapping, registry, + dictionaryService); + } + } + } +} diff --git a/src/main/java/org/alfresco/opencmis/dictionary/TypeDefinitionWrapper.java b/src/main/java/org/alfresco/opencmis/dictionary/TypeDefinitionWrapper.java new file mode 100644 index 0000000..59a2f1e --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/dictionary/TypeDefinitionWrapper.java @@ -0,0 +1,69 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.dictionary; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition; +import org.apache.chemistry.opencmis.commons.enums.Action; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; + +public interface TypeDefinitionWrapper +{ + TypeDefinition getTypeDefinition(boolean includePropertyDefinitions); + + String getTypeId(); + + BaseTypeId getBaseTypeId(); + + boolean isBaseType(); + + QName getAlfrescoName(); + + QName getAlfrescoClass(); + String getTenantId(); + TypeDefinitionWrapper getParent(); + +// List getChildren(); + + Collection getProperties(); + + Collection getProperties(boolean update); + + PropertyDefinitionWrapper getPropertyById(String propertyId); + + PropertyDefinitionWrapper getPropertyByQueryName(String queryName); + + PropertyDefinitionWrapper getPropertyByQName(QName name); + + Map getActionEvaluators(); + + void updateDefinition(DictionaryService dictionaryService); +} diff --git a/src/main/java/org/alfresco/opencmis/mapping/AbstractSimpleLuceneBuilder.java b/src/main/java/org/alfresco/opencmis/mapping/AbstractSimpleLuceneBuilder.java new file mode 100644 index 0000000..91b1f34 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/mapping/AbstractSimpleLuceneBuilder.java @@ -0,0 +1,138 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.mapping; + +import java.io.Serializable; + +import org.alfresco.repo.search.adaptor.lucene.AnalysisMode; +import org.alfresco.repo.search.adaptor.lucene.LuceneFunction; +import org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor; +import org.alfresco.repo.search.impl.querymodel.PredicateMode; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.namespace.QName; + +/** + * Common support for lucene query building. + * + * @author davidc + */ +public abstract class AbstractSimpleLuceneBuilder extends BaseLuceneBuilder +{ + /** + * Construct + */ + protected AbstractSimpleLuceneBuilder() + { + super(); + } + + protected abstract String getValueAsString(Serializable value); + + protected String getRangeMax() + { + return "\uFFFF"; + } + + protected String getRangeMin() + { + return "\u0000"; + } + + protected abstract DataTypeDefinition getInDataType(); + + protected abstract QName getQNameForExists(); + + @Override + public Q buildLuceneEquality(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + return lqpa.getFieldQuery(getLuceneFieldName(), getValueAsString(value), AnalysisMode.IDENTIFIER, luceneFunction); + } + + @Override + public Q buildLuceneExists(LuceneQueryParserAdaptor lqpa, Boolean not) throws E + { + if (not) + { + return lqpa.getFieldQuery("ISNULL", getQNameForExists().toString(), AnalysisMode.DEFAULT, LuceneFunction.FIELD); + } + else + { + return lqpa.getFieldQuery("ISNOTNULL", getQNameForExists().toString(), AnalysisMode.DEFAULT, LuceneFunction.FIELD); + } + } + + @Override + public Q buildLuceneGreaterThan(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + String field = getLuceneFieldName(); + String stringValue = getValueAsString(value); + return lqpa.getRangeQuery(field, stringValue, getRangeMax(), false, true, AnalysisMode.IDENTIFIER, luceneFunction); + } + + @Override + public Q buildLuceneGreaterThanOrEquals(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + String field = getLuceneFieldName(); + String stringValue = getValueAsString(value); + return lqpa.getRangeQuery(field, stringValue, getRangeMax(), true, true, AnalysisMode.IDENTIFIER, luceneFunction); + } + + @Override + public Q buildLuceneLessThan(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + String field = getLuceneFieldName(); + String stringValue = getValueAsString(value); + return lqpa.getRangeQuery(field, getRangeMin(), stringValue, true, false, AnalysisMode.IDENTIFIER, luceneFunction); + } + + @Override + public Q buildLuceneLessThanOrEquals(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + String field = getLuceneFieldName(); + String stringValue = getValueAsString(value); + return lqpa.getRangeQuery(field, getRangeMin(), stringValue, true, true, AnalysisMode.IDENTIFIER, luceneFunction); + } + + @Override + public Q buildLuceneLike(LuceneQueryParserAdaptor lqpa, Serializable value, Boolean not) throws E + { + String field = getLuceneFieldName(); + String stringValue = getValueAsString(value); + + Q q = lqpa.getLikeQuery(field, stringValue, AnalysisMode.IDENTIFIER); + if (not) + { + q = lqpa.getNegatedQuery(q); + } + return q; + } + + @Override + public String getLuceneSortField(LuceneQueryParserAdaptor lqpa) throws E + { + return getLuceneFieldName(); + } +} diff --git a/src/main/java/org/alfresco/opencmis/mapping/BaseLuceneBuilder.java b/src/main/java/org/alfresco/opencmis/mapping/BaseLuceneBuilder.java new file mode 100644 index 0000000..ddb8e15 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/mapping/BaseLuceneBuilder.java @@ -0,0 +1,129 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.mapping; + +import java.io.Serializable; +import java.util.Collection; + +import org.alfresco.opencmis.dictionary.CMISPropertyLuceneBuilder; +import org.alfresco.repo.search.adaptor.lucene.LuceneFunction; +import org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor; +import org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserExpressionAdaptor; +import org.alfresco.repo.search.impl.querymodel.PredicateMode; + +/** + * Base class for all property lucene builders + * + * @author davidc + */ +public class BaseLuceneBuilder implements CMISPropertyLuceneBuilder +{ + /** + * Construct + * + */ + protected BaseLuceneBuilder() + { + } + + @Override + public Q buildLuceneEquality(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + throw new UnsupportedOperationException(); + } + + @Override + public Q buildLuceneExists(LuceneQueryParserAdaptor lqpa, Boolean not) throws E + { + throw new UnsupportedOperationException(); + } + + @Override + public Q buildLuceneGreaterThan(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + throw new UnsupportedOperationException(); + } + + @Override + public Q buildLuceneGreaterThanOrEquals(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + throw new UnsupportedOperationException(); + } + + @Override + public Q buildLuceneIn(LuceneQueryParserAdaptor lqpa, Collection values, Boolean not, PredicateMode mode) throws E + { + LuceneQueryParserExpressionAdaptor expressionAdaptor = lqpa.getExpressionAdaptor(); + for(Serializable value : values) + { + expressionAdaptor.addOptional(buildLuceneEquality(lqpa, value, mode, LuceneFunction.FIELD)); + } + if(not) + { + return expressionAdaptor.getNegatedQuery(); + } + else + { + return expressionAdaptor.getQuery(); + } + } + + @Override + public Q buildLuceneInequality(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + return lqpa.getNegatedQuery(buildLuceneEquality(lqpa, value, mode, luceneFunction)); + } + + @Override + public Q buildLuceneLessThan(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + throw new UnsupportedOperationException(); + } + + @Override + public Q buildLuceneLessThanOrEquals(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + throw new UnsupportedOperationException(); + } + + @Override + public Q buildLuceneLike(LuceneQueryParserAdaptor lqpa, Serializable value, Boolean not) throws E + { + throw new UnsupportedOperationException(); + } + + @Override + public String getLuceneFieldName() + { + throw new UnsupportedOperationException(); + } + + @Override + public String getLuceneSortField(LuceneQueryParserAdaptor lqpa) throws E + { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/org/alfresco/opencmis/mapping/BaseTypeIdLuceneBuilder.java b/src/main/java/org/alfresco/opencmis/mapping/BaseTypeIdLuceneBuilder.java new file mode 100644 index 0000000..14e100e --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/mapping/BaseTypeIdLuceneBuilder.java @@ -0,0 +1,100 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.mapping; + +import java.io.Serializable; + +import org.alfresco.opencmis.dictionary.CMISDictionaryService; +import org.alfresco.opencmis.dictionary.TypeDefinitionWrapper; +import org.alfresco.repo.search.adaptor.lucene.AnalysisMode; +import org.alfresco.repo.search.adaptor.lucene.LuceneFunction; +import org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor; +import org.alfresco.repo.search.impl.querymodel.PredicateMode; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; + + +/** + * Get the CMIS object type id property + * + * @author andyh + */ +public class BaseTypeIdLuceneBuilder extends BaseLuceneBuilder +{ + private CMISDictionaryService dictionaryService; + + + /** + * Construct + */ + public BaseTypeIdLuceneBuilder(CMISDictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + @Override + public Q buildLuceneEquality(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + return lqpa.getFieldQuery("TYPE", getType(getValueAsString(value)), AnalysisMode.IDENTIFIER, luceneFunction); + } + + @Override + public Q buildLuceneExists(LuceneQueryParserAdaptor lqpa, Boolean not) throws E + { + if (not) + { + return lqpa.getMatchNoneQuery(); + } + else + { + return lqpa.getMatchAllQuery(); + } + } + + private String getType(String tableName) + { + TypeDefinitionWrapper typeDef = dictionaryService.findTypeByQueryName(tableName); + if (typeDef == null) + { + throw new CmisInvalidArgumentException("Unknown type: " + tableName); + } + if(!typeDef.isBaseType()) + { + throw new CmisInvalidArgumentException("Not a base type: " + tableName); + } + if(!typeDef.getTypeDefinition(false).isQueryable()) + { + throw new CmisInvalidArgumentException("Type is not queryable: " + tableName); + } + return typeDef.getAlfrescoClass().toString(); + } + + private String getValueAsString(Serializable value) + { + String asString = DefaultTypeConverter.INSTANCE.convert(String.class, value); + return asString; + } +} diff --git a/src/main/java/org/alfresco/opencmis/mapping/CMISMapping.java b/src/main/java/org/alfresco/opencmis/mapping/CMISMapping.java new file mode 100644 index 0000000..951d2b1 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/mapping/CMISMapping.java @@ -0,0 +1,860 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.mapping; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.opencmis.CMISAccessControlFormatEnum; +import org.alfresco.opencmis.dictionary.QNameFilter; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; +import org.apache.chemistry.opencmis.commons.enums.PropertyType; +import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; +import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException; +import org.springframework.beans.factory.InitializingBean; + +/** + * CMIS <-> Alfresco mappings. It additionally excludes a list of QNames based + * on a user defined list + * + * @author andyh + */ +public class CMISMapping implements InitializingBean +{ + + /** + * The Alfresco CMIS Namespace + */ + public static String CMIS_MODEL_NS = "cmis"; + public static String CMIS_MODEL_URI = "http://www.alfresco.org/model/cmis/1.0/cs01"; + + public static String CMIS_EXT_NS = "cmisext"; + public static String CMIS_EXT_URI = "http://www.alfresco.org/model/cmis/1.0/cs01ext"; + + /** + * The Alfresco CMIS Model name. + */ + public static String CMIS_MODEL_NAME = "cmismodel"; + + /** + * The QName for the Alfresco CMIS Model. + */ + public static QName CMIS_MODEL_QNAME = QName.createQName(CMIS_MODEL_URI, CMIS_MODEL_NAME); + + // CMIS Data Types + public static QName CMIS_DATATYPE_ID = QName.createQName(CMIS_MODEL_URI, "id"); + public static QName CMIS_DATATYPE_URI = QName.createQName(CMIS_MODEL_URI, "uri"); + public static QName CMIS_DATATYPE_XML = QName.createQName(CMIS_MODEL_URI, "xml"); + public static QName CMIS_DATATYPE_HTML = QName.createQName(CMIS_MODEL_URI, "html"); + + // CMIS Types + public static QName OBJECT_QNAME = QName.createQName(CMIS_EXT_URI, "object"); + public static QName DOCUMENT_QNAME = QName.createQName(CMIS_MODEL_URI, "document"); + public static QName FOLDER_QNAME = QName.createQName(CMIS_MODEL_URI, "folder"); + public static QName RELATIONSHIP_QNAME = QName.createQName(CMIS_MODEL_URI, "relationship"); + public static QName POLICY_QNAME = QName.createQName(CMIS_MODEL_URI, "policy"); + public static QName SECONDARY_TYPES_QNAME = QName.createQName(CMIS_MODEL_URI, "secondary"); // cmis 1.1 + public static QName ASPECTS_QNAME = QName.createQName(CMIS_EXT_URI, "aspects"); // cmis 1.0 + public static QName ITEM_QNAME = QName.createQName(CMIS_MODEL_URI, "item"); // cmis 1.1 + + // CMIS Internal Type Ids + public static String OBJECT_TYPE_ID = "cmisext:object"; + + /** + * Basic permissions. + */ + public static final String CMIS_READ = "cmis:read"; + public static final String CMIS_WRITE = "cmis:write"; + public static final String CMIS_ALL = "cmis:all"; + + // Service Dependencies + protected DictionaryService dictionaryService; + protected NamespaceService namespaceService; + + // Mappings + private Map mapAlfrescoQNameToTypeId = new HashMap(); + private Map mapCmisQNameToAlfrescoQName = new HashMap(); + private Map mapAlfrescoQNameToCmisQName = new HashMap(); + private Map mapAlfrescoToCmisDataType = new HashMap(); + private Map mapCmisDataTypeToAlfresco = new HashMap(); + + private QNameFilter filter; + + private CmisVersion cmisVersion; + + public void setCmisVersion(CmisVersion cmisVersion) + { + this.cmisVersion = cmisVersion; + } + + public CmisVersion getCmisVersion() + { + return cmisVersion; + } + + /* + * (non-Javadoc) + * + * @see + * org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() + { + // + // Type Mappings + // + + mapAlfrescoQNameToTypeId.put(OBJECT_QNAME, OBJECT_TYPE_ID); + mapAlfrescoQNameToTypeId.put(DOCUMENT_QNAME, BaseTypeId.CMIS_DOCUMENT.value()); + mapAlfrescoQNameToTypeId.put(FOLDER_QNAME, BaseTypeId.CMIS_FOLDER.value()); + mapAlfrescoQNameToTypeId.put(RELATIONSHIP_QNAME, BaseTypeId.CMIS_RELATIONSHIP.value()); + mapAlfrescoQNameToTypeId.put(SECONDARY_TYPES_QNAME, BaseTypeId.CMIS_SECONDARY.value()); + mapAlfrescoQNameToTypeId.put(ITEM_QNAME, BaseTypeId.CMIS_ITEM.value()); + mapAlfrescoQNameToTypeId.put(POLICY_QNAME, BaseTypeId.CMIS_POLICY.value()); + + mapAlfrescoQNameToCmisQName.put(ContentModel.TYPE_CONTENT, DOCUMENT_QNAME); + mapAlfrescoQNameToCmisQName.put(ContentModel.TYPE_FOLDER, FOLDER_QNAME); + mapAlfrescoQNameToCmisQName.put(ContentModel.TYPE_BASE, ITEM_QNAME); + + mapCmisQNameToAlfrescoQName.put(DOCUMENT_QNAME, ContentModel.TYPE_CONTENT); + mapCmisQNameToAlfrescoQName.put(FOLDER_QNAME, ContentModel.TYPE_FOLDER); + mapCmisQNameToAlfrescoQName.put(ITEM_QNAME, ContentModel.TYPE_BASE); + mapCmisQNameToAlfrescoQName.put(RELATIONSHIP_QNAME, null); + mapCmisQNameToAlfrescoQName.put(POLICY_QNAME, null); + + // + // Data Type Mappings + // + + mapAlfrescoToCmisDataType.put(DataTypeDefinition.ANY, null); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.ENCRYPTED, null); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.ASSOC_REF, null); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.BOOLEAN, PropertyType.BOOLEAN); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.CATEGORY, PropertyType.ID); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.CHILD_ASSOC_REF, null); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.CONTENT, null); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.DATE, PropertyType.DATETIME); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.DATETIME, PropertyType.DATETIME); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.DOUBLE, PropertyType.DECIMAL); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.FLOAT, PropertyType.DECIMAL); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.INT, PropertyType.INTEGER); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.LOCALE, null); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.PERIOD, null); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.LONG, PropertyType.INTEGER); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.MLTEXT, PropertyType.STRING); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.NODE_REF, PropertyType.ID); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.PATH, null); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.QNAME, null); + mapAlfrescoToCmisDataType.put(DataTypeDefinition.TEXT, PropertyType.STRING); + mapAlfrescoToCmisDataType.put(CMIS_DATATYPE_ID, PropertyType.ID); + mapAlfrescoToCmisDataType.put(CMIS_DATATYPE_URI, PropertyType.URI); + mapAlfrescoToCmisDataType.put(CMIS_DATATYPE_HTML, PropertyType.HTML); + + mapCmisDataTypeToAlfresco.put(PropertyType.ID, DataTypeDefinition.TEXT); + mapCmisDataTypeToAlfresco.put(PropertyType.INTEGER, DataTypeDefinition.LONG); + mapCmisDataTypeToAlfresco.put(PropertyType.STRING, DataTypeDefinition.TEXT); + mapCmisDataTypeToAlfresco.put(PropertyType.DECIMAL, DataTypeDefinition.DOUBLE); + mapCmisDataTypeToAlfresco.put(PropertyType.BOOLEAN, DataTypeDefinition.BOOLEAN); + mapCmisDataTypeToAlfresco.put(PropertyType.DATETIME, DataTypeDefinition.DATETIME); + mapCmisDataTypeToAlfresco.put(PropertyType.URI, DataTypeDefinition.TEXT); + mapCmisDataTypeToAlfresco.put(PropertyType.HTML, DataTypeDefinition.TEXT); + } + + public void setFilter(QNameFilter filter) + { + this.filter = filter; + } + + /** + * @param dictionaryService dictionaryService + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * @param namespaceService service + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * @return namespaceService + */ + public NamespaceService getNamespaceService() + { + return namespaceService; + } + + /** + * @return dictionaryService + */ + public DictionaryService getDictionaryService() + { + return dictionaryService; + } + + /* + * Is the type excluded from the CMIS dictionary and therefore not visible to clients? + */ + public boolean isExcluded(QName typeQName) + { + boolean isExcluded = false; + + // check for exclusion of the type and, if necessary, its parents + if(filter != null && typeQName != null) + { + isExcluded = filter.isExcluded(typeQName); + if(!isExcluded) + { + + // check parent, if any + AspectDefinition aspectDef = dictionaryService.getAspect(typeQName); + QName parentType = null; + if(aspectDef != null) + { + parentType = aspectDef.getParentName(); + } + else + { + TypeDefinition typeDef = dictionaryService.getType(typeQName); + if(typeDef != null) + { + parentType = typeDef.getParentName(); + } + else + { + parentType = null; + } + } + if(parentType != null) + { + isExcluded = isExcluded(parentType); + } + } + + filter.setExcluded(typeQName, Boolean.valueOf(isExcluded)); + } + + return isExcluded; + } + + /** + * Gets the CMIS Type Id given the Alfresco QName for the type in any + * Alfresco model + * + * @param scope BaseTypeId + * @param typeQName QName + * @return String + */ + public String getCmisTypeId(BaseTypeId scope, QName typeQName) + { + String typeId = mapAlfrescoQNameToTypeId.get(typeQName); + if (typeId == null) + { + String p = null; + switch (scope) + { + case CMIS_DOCUMENT: + p = "D"; + break; + case CMIS_FOLDER: + p = "F"; + break; + case CMIS_RELATIONSHIP: + p = "R"; + break; + case CMIS_SECONDARY: + p = "P"; + break; + case CMIS_POLICY: + p = "P"; + break; + case CMIS_ITEM: + p = "I"; + break; + default: + throw new CmisRuntimeException("Invalid base type!"); + } + + return p + ":" + typeQName.toPrefixString(namespaceService); + } + else + { + return typeId; + } + } + + public String getCmisTypeId(QName classQName) + { + if (classQName.equals(ContentModel.TYPE_CONTENT)) + { + return getCmisTypeId(BaseTypeId.CMIS_DOCUMENT, classQName); + } + if (classQName.equals(ContentModel.TYPE_FOLDER)) + { + return getCmisTypeId(BaseTypeId.CMIS_FOLDER, classQName); + } + if (classQName.equals(CMISMapping.RELATIONSHIP_QNAME)) + { + return getCmisTypeId(BaseTypeId.CMIS_RELATIONSHIP, classQName); + } + if (classQName.equals(CMISMapping.POLICY_QNAME)) + { + return getCmisTypeId(BaseTypeId.CMIS_POLICY, classQName); + } + if (classQName.equals(CMISMapping.ASPECTS_QNAME)) + { + return getCmisTypeId(BaseTypeId.CMIS_POLICY, classQName); + } + if (isValidCmisDocument(classQName)) + { + return getCmisTypeId(BaseTypeId.CMIS_DOCUMENT, classQName); + } + if (isValidCmisFolder(classQName)) + { + return getCmisTypeId(BaseTypeId.CMIS_FOLDER, classQName); + } + if (isValidCmisRelationship(classQName)) + { + return getCmisTypeId(BaseTypeId.CMIS_RELATIONSHIP, classQName); + } + if (cmisVersion.equals(CmisVersion.CMIS_1_1) && isValidCmisSecondaryType(classQName)) + { + return getCmisTypeId(BaseTypeId.CMIS_SECONDARY, classQName); + } + if (cmisVersion.equals(CmisVersion.CMIS_1_1) && isValidCmisItem(classQName)) + { + return getCmisTypeId(BaseTypeId.CMIS_ITEM, classQName); + } + if (cmisVersion.equals(CmisVersion.CMIS_1_0) && isValidCmisPolicy(classQName)) + { + return getCmisTypeId(BaseTypeId.CMIS_POLICY, classQName); + } + + return null; + } + + public String buildPrefixEncodedString(QName qname) + { + return qname.toPrefixString(namespaceService); + } + + public QName getAlfrescoName(String typeId) + { + // Is it an Alfresco type id? + if (typeId.length() < 4 || typeId.charAt(1) != ':') + { + throw new CmisInvalidArgumentException("Malformed type id '" + typeId + "'"); + } + + return QName.createQName(typeId.substring(2), namespaceService); + } + + public boolean isValidCmisObject(BaseTypeId scope, QName qname) + { + switch (scope) + { + case CMIS_DOCUMENT: + return isValidCmisDocument(qname); + case CMIS_FOLDER: + return isValidCmisFolder(qname); + case CMIS_POLICY: + return isValidCmisPolicy(qname); + case CMIS_RELATIONSHIP: + return isValidCmisRelationship(qname); + case CMIS_SECONDARY: + return isValidCmisSecondaryType(qname); + case CMIS_ITEM: + return isValidCmisItem(qname); + } + + return false; + } + + /** + * Is this a valid CMIS folder type? + * + * @param typeQName QName + * @return boolean + */ + public boolean isValidCmisFolder(QName typeQName) + { + if(isExcluded(typeQName)) + { + return false; + } + + if (typeQName == null) + { + return false; + } + if (typeQName.equals(FOLDER_QNAME)) + { + return true; + } + + if (dictionaryService.isSubClass(typeQName, ContentModel.TYPE_FOLDER)) + { + if (typeQName.equals(ContentModel.TYPE_FOLDER)) + { + return false; + } else + { + return true; + } + } + + return false; + } + + /** + * Is this a valid CMIS document type? + * + * @param typeQName QName + * @return boolean + */ + public boolean isValidCmisDocument(QName typeQName) + { + if(isExcluded(typeQName)) + { + return false; + } + + if (typeQName == null) + { + return false; + } + if (typeQName.equals(DOCUMENT_QNAME)) + { + return true; + } + + if (dictionaryService.isSubClass(typeQName, ContentModel.TYPE_CONTENT)) + { + if (typeQName.equals(ContentModel.TYPE_CONTENT)) + { + return false; + } else + { + return true; + } + } + return false; + } + + /** + * Is this a valid CMIS secondary type? + * + * @param typeQName QName + * @return boolean + */ + public boolean isValidCmisSecondaryType(QName typeQName) + { + if(isExcluded(typeQName)) + { + return false; + } + + if (typeQName == null) + { + return false; + } + + if (typeQName.equals(SECONDARY_TYPES_QNAME)) + { + return true; + } + + AspectDefinition aspectDef = dictionaryService.getAspect(typeQName); + if (aspectDef == null) + { + return false; + } + + // Anything derived from the aspects here would at some point have to linked up with an invalid parent so exclude these aspects + // AND any that are derived from them. + if ( dictionaryService.isSubClass(aspectDef.getName(), ContentModel.ASPECT_VERSIONABLE) + || dictionaryService.isSubClass(aspectDef.getName(), ContentModel.ASPECT_AUDITABLE) + || dictionaryService.isSubClass(aspectDef.getName(), ContentModel.ASPECT_REFERENCEABLE)) + { + return false; + } + return true; + } + + /** + * Is this a valid CMIS policy type? + * + * @param typeQName QName + * @return boolean + */ + public boolean isValidCmisPolicy(QName typeQName) + { if (typeQName == null) + { + return false; + } + if (typeQName.equals(POLICY_QNAME)) + { + return true; + } + + if(cmisVersion.equals(CmisVersion.CMIS_1_0)) + { + if (typeQName.equals(ASPECTS_QNAME)) + { + return true; + } + + AspectDefinition aspectDef = dictionaryService.getAspect(typeQName); + if (aspectDef == null) + { + return false; + } + + // Anything derived from the aspects here would at some point have to linked up with an invalid parent so exclude these aspects + // AND any that are derived from them. + if ( dictionaryService.isSubClass(aspectDef.getName(), ContentModel.ASPECT_VERSIONABLE) + || dictionaryService.isSubClass(aspectDef.getName(), ContentModel.ASPECT_AUDITABLE) + || dictionaryService.isSubClass(aspectDef.getName(), ContentModel.ASPECT_REFERENCEABLE)) + { + return false; + } + return true; + } + else + { + return false; + } + } + + /** + * Is an association valid in CMIS? It must be a non-child relationship and + * the source and target must both be valid CMIS types. + * + * @param associationQName QName + * @return boolean + */ + public boolean isValidCmisRelationship(QName associationQName) + { + if (associationQName == null) + { + return false; + } + if (associationQName.equals(RELATIONSHIP_QNAME)) + { + return true; + } + AssociationDefinition associationDefinition = dictionaryService.getAssociation( + associationQName); + if (associationDefinition == null) + { + return false; + } + if (associationDefinition.isChild()) + { + return false; + } + if(!isValidCmisRelationshipEndPoint(associationDefinition.getTargetClass().getName())) + { + return false; + } + if(!isValidCmisRelationshipEndPoint(associationDefinition.getSourceClass().getName())) + { + return false; + } + return true; + } + + public boolean isValidCmisRelationshipEndPoint(QName typeQName) + { + if(dictionaryService.getClass(typeQName).isAspect()) + { + return true; + } + + if (typeQName.equals(FOLDER_QNAME)) + { + return true; + } + + + if (typeQName.equals(DOCUMENT_QNAME)) + { + return true; + } + + if (dictionaryService.isSubClass(typeQName, ContentModel.TYPE_BASE)) + { + return true; + } + return false; + } + + /** + * Is this a valid CMIS item type? + * + * @param typeQName QName + * @return boolean + */ + public boolean isValidCmisItem(QName typeQName) + { + if(isExcluded(typeQName)) + { + return false; + } + + if (typeQName == null) + { + return false; + } + + if(typeQName.equals(ITEM_QNAME)) + { + return true; + } + + if(typeQName.equals(ContentModel.TYPE_BASE)) + { + return false; + } + + AspectDefinition aspectDef = dictionaryService.getAspect(typeQName); + if (aspectDef != null) + { + // aspects are not items - this stops warning from getType + return false; + } + + TypeDefinition typeDef = dictionaryService.getType(typeQName); + if (typeDef == null) + { + // type does not exist + return false; + } + + if (dictionaryService.isSubClass(typeQName, ContentModel.TYPE_BASE)) + { + if(dictionaryService.isSubClass(typeQName, ContentModel.TYPE_FOLDER)) + { + return false; + } + if(dictionaryService.isSubClass(typeQName, ContentModel.TYPE_CONTENT)) + { + return false; + } + + return true; + } + + return false; + } + + + + /** + * Given an Alfresco model type map it to the appropriate type. Maps + * cm:folder and cm:content to the CMIS definitions + */ + public QName getCmisType(QName typeQName) + { + QName mapped = mapAlfrescoQNameToCmisQName.get(typeQName); + if (mapped != null) + { + return mapped; + } + return typeQName; + } + + /** + * Is Alfresco Type mapped to an alternative CMIS Type? + */ + public boolean isRemappedType(QName typeQName) + { + return mapAlfrescoQNameToCmisQName.containsKey(typeQName); + } + + /** + * Given a CMIS model type map it to the appropriate Alfresco type. + * + * @param cmisTypeQName QName + * @return QName + */ + public QName getAlfrescoClass(QName cmisTypeQName) + { + QName mapped = mapCmisQNameToAlfrescoQName.get(cmisTypeQName); + if (mapped != null) + { + return mapped; + } + return cmisTypeQName; + } + + /** + * Get the CMIS property type for a property + * + * @param datatype DataTypeDefinition + * @return PropertyType + */ + public PropertyType getDataType(DataTypeDefinition datatype) + { + return getDataType(datatype.getName()); + } + + public PropertyType getDataType(QName dataType) + { + return mapAlfrescoToCmisDataType.get(dataType); + } + + public QName getAlfrescoDataType(PropertyType propertyType) + { + return mapCmisDataTypeToAlfresco.get(propertyType); + } + + /** + * @param propertyQName QName + * @return String + */ + public String getCmisPropertyId(QName propertyQName) + { + return propertyQName.toPrefixString(namespaceService); + } + + public Collection> getReportedPermissions(String permission, Set permissions, + boolean hasFull, boolean isDirect, CMISAccessControlFormatEnum format) + { + ArrayList> answer = new ArrayList>(20); + // indirect + + if (hasFull) + { + answer.add(new Pair(CMIS_READ, false)); + answer.add(new Pair(CMIS_WRITE, false)); + answer.add(new Pair(CMIS_ALL, false)); + } + + for (String perm : permissions) + { + if (PermissionService.READ.equals(perm)) + { + answer.add(new Pair(CMIS_READ, false)); + } else if (PermissionService.WRITE.equals(perm)) + { + answer.add(new Pair(CMIS_WRITE, false)); + } else if (PermissionService.ALL_PERMISSIONS.equals(perm)) + { + answer.add(new Pair(CMIS_READ, false)); + answer.add(new Pair(CMIS_WRITE, false)); + answer.add(new Pair(CMIS_ALL, false)); + } + + if (hasFull) + { + answer.add(new Pair(CMIS_READ, false)); + answer.add(new Pair(CMIS_WRITE, false)); + answer.add(new Pair(CMIS_ALL, false)); + } + } + + // permission + + if (format == CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS) + { + if (PermissionService.READ.equals(permission)) + { + answer.add(new Pair(CMIS_READ, false)); + answer.add(new Pair(permission, isDirect)); + } else if (PermissionService.WRITE.equals(permission)) + { + answer.add(new Pair(CMIS_WRITE, false)); + answer.add(new Pair(permission, isDirect)); + } else if (PermissionService.ALL_PERMISSIONS.equals(permission)) + { + answer.add(new Pair(CMIS_ALL, false)); + answer.add(new Pair(permission, isDirect)); + } else + { + answer.add(new Pair(permission, isDirect)); + } + } else if (format == CMISAccessControlFormatEnum.CMIS_BASIC_PERMISSIONS) + { + if (PermissionService.READ.equals(permission)) + { + answer.add(new Pair(CMIS_READ, isDirect)); + } else if (PermissionService.WRITE.equals(permission)) + { + answer.add(new Pair(CMIS_WRITE, isDirect)); + } else if (PermissionService.ALL_PERMISSIONS.equals(permission)) + { + answer.add(new Pair(CMIS_ALL, isDirect)); + } else + { + // else nothing + } + } + + return answer; + } + + /** + * @param permission String + * @return permission to set + */ + public String getSetPermission(String permission) + { + if (permission.equals(CMIS_READ)) + { + return PermissionService.READ; + } else if (permission.equals(CMIS_WRITE)) + { + return PermissionService.WRITE; + } else if (permission.equals(CMIS_ALL)) + { + return PermissionService.ALL_PERMISSIONS; + } else + { + return permission; + } + } + +} diff --git a/src/main/java/org/alfresco/opencmis/mapping/ContentStreamLengthLuceneBuilder.java b/src/main/java/org/alfresco/opencmis/mapping/ContentStreamLengthLuceneBuilder.java new file mode 100644 index 0000000..3b8e58e --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/mapping/ContentStreamLengthLuceneBuilder.java @@ -0,0 +1,85 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.mapping; + +import java.io.Serializable; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.namespace.QName; + +/** + * Lucene Builder for CMIS content stream length property + * + * @author andyh + */ +public class ContentStreamLengthLuceneBuilder extends AbstractSimpleLuceneBuilder +{ + private DictionaryService dictionaryService; + + /** + * Construct + * + * @param dictionaryService DictionaryService + */ + public ContentStreamLengthLuceneBuilder(DictionaryService dictionaryService) + { + super(); + this.dictionaryService = dictionaryService; + } + + @Override + public String getLuceneFieldName() + { + StringBuilder field = new StringBuilder(128); + field.append("@"); + field.append(ContentModel.PROP_CONTENT); + field.append(".size"); + return field.toString(); + } + + @Override + protected String getValueAsString(Serializable value) + { + Object converted = DefaultTypeConverter.INSTANCE.convert(dictionaryService.getDataType(DataTypeDefinition.LONG), value); + String asString = DefaultTypeConverter.INSTANCE.convert(String.class, converted); + return asString; + } + + @Override + protected QName getQNameForExists() + { + return ContentModel.PROP_CONTENT; + } + + @Override + protected DataTypeDefinition getInDataType() + { + return dictionaryService.getDataType(DataTypeDefinition.LONG); + } +} diff --git a/src/main/java/org/alfresco/opencmis/mapping/ContentStreamMimetypeLuceneBuilder.java b/src/main/java/org/alfresco/opencmis/mapping/ContentStreamMimetypeLuceneBuilder.java new file mode 100644 index 0000000..89faca9 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/mapping/ContentStreamMimetypeLuceneBuilder.java @@ -0,0 +1,85 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.mapping; + +import java.io.Serializable; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.namespace.QName; + +/** + * Lucene Builder for CMIS content stream mimetype property + * + * @author andyh + */ +public class ContentStreamMimetypeLuceneBuilder extends AbstractSimpleLuceneBuilder +{ + private DictionaryService dictionaryService; + + /** + * Construct + * + * @param dictionaryService DictionaryService + */ + public ContentStreamMimetypeLuceneBuilder(DictionaryService dictionaryService) + { + super(); + this.dictionaryService = dictionaryService; + } + + @Override + public String getLuceneFieldName() + { + StringBuilder field = new StringBuilder(128); + field.append("@"); + field.append(ContentModel.PROP_CONTENT); + field.append(".mimetype"); + return field.toString(); + } + + @Override + protected String getValueAsString(Serializable value) + { + Object converted = DefaultTypeConverter.INSTANCE.convert(dictionaryService.getDataType(DataTypeDefinition.TEXT), value); + String asString = DefaultTypeConverter.INSTANCE.convert(String.class, converted); + return asString; + } + + @Override + protected QName getQNameForExists() + { + return ContentModel.PROP_CONTENT; + } + + @Override + protected DataTypeDefinition getInDataType() + { + return dictionaryService.getDataType(DataTypeDefinition.TEXT); + } +} diff --git a/src/main/java/org/alfresco/opencmis/mapping/DirectLuceneBuilder.java b/src/main/java/org/alfresco/opencmis/mapping/DirectLuceneBuilder.java new file mode 100644 index 0000000..37c2437 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/mapping/DirectLuceneBuilder.java @@ -0,0 +1,143 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.mapping; + +import java.io.Serializable; + +import org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; + +/** + * A simple 1-1 property lucene builder mapping from a CMIS property name to an alfresco property + * + * @author andyh + */ +public class DirectLuceneBuilder extends AbstractSimpleLuceneBuilder +{ + private DictionaryService dictionaryService; + private QName alfrescoName; + + public DirectLuceneBuilder(DictionaryService dictionaryService, QName alfrescoName) + { + this.dictionaryService = dictionaryService; + this.alfrescoName = alfrescoName; + } + + @Override + public String getLuceneSortField(LuceneQueryParserAdaptor lqpa) throws E + { + String field = getLuceneFieldName(); + // need to find the real field to use + PropertyDefinition propertyDef = dictionaryService.getProperty(QName.createQName(field.substring(1))); + + if (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT)) + { + throw new CmisInvalidArgumentException("Order on content properties is not curently supported"); + } + else if ((propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)) || (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) + { + field = lqpa.getSortField(field); + } + else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME)) + { + field = lqpa.getDatetimeSortField(field, propertyDef); + } + + return field; + } + + @Override + public String getLuceneFieldName() + { + StringBuilder field = new StringBuilder(64); + field.append("@"); + field.append(alfrescoName); + return field.toString(); + } + + @Override + protected String getValueAsString(Serializable value) + { + PropertyDefinition pd = dictionaryService.getProperty(alfrescoName); + Object converted = DefaultTypeConverter.INSTANCE.convert(pd.getDataType(), value); + String asString = DefaultTypeConverter.INSTANCE.convert(String.class, converted); + return asString; + } + + @Override + protected QName getQNameForExists() + { + return alfrescoName; + } + + @Override + protected DataTypeDefinition getInDataType() + { + PropertyDefinition pd = dictionaryService.getProperty(alfrescoName); + return pd.getDataType(); + } + + @Override + protected String getRangeMax() + { + if(getInDataType().getName().equals(DataTypeDefinition.DATE)) + { + return "MAX"; + } + else if(getInDataType().getName().equals(DataTypeDefinition.DATETIME)) + { + return "MAX"; + } + else + { + return super.getRangeMax(); + } + } + + + @Override + protected String getRangeMin() + { + if(getInDataType().getName().equals(DataTypeDefinition.DATE)) + { + return "MIN"; + } + else if(getInDataType().getName().equals(DataTypeDefinition.DATETIME)) + { + return "MIN"; + } + else + { + return super.getRangeMin(); + } + } + +} diff --git a/src/main/java/org/alfresco/opencmis/mapping/FixedValueLuceneBuilder.java b/src/main/java/org/alfresco/opencmis/mapping/FixedValueLuceneBuilder.java new file mode 100644 index 0000000..09c3979 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/mapping/FixedValueLuceneBuilder.java @@ -0,0 +1,269 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.mapping; + +import java.io.Serializable; +import java.util.Collection; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.alfresco.repo.search.adaptor.lucene.LuceneFunction; +import org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor; +import org.alfresco.repo.search.impl.querymodel.PredicateMode; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.util.EqualsHelper; +import org.alfresco.util.SearchLanguageConversion; + +/** + * Property lucene builder for fixed value mapping (eg to null, true, etc) + * + * @author andyh + */ +public class FixedValueLuceneBuilder extends BaseLuceneBuilder +{ + private Serializable value; + + /** + * Construct + * + * @param value Serializable + */ + public FixedValueLuceneBuilder(Serializable value) + { + super(); + this.value = value; + } + + @Override + public Q buildLuceneEquality(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + if (EqualsHelper.nullSafeEquals(value, value)) + { + return lqpa.getMatchAllQuery(); + } + else + { + return lqpa.getMatchNoneQuery(); + } + } + + @Override + public Q buildLuceneExists(LuceneQueryParserAdaptor lqpa, Boolean not) throws E + { + if (not) + { + if (value == null) + { + return lqpa.getMatchAllQuery(); + } + else + { + return lqpa.getMatchNoneQuery(); + } + } + else + { + if (value == null) + { + return lqpa.getMatchNoneQuery(); + } + else + { + return lqpa.getMatchAllQuery(); + } + } + + } + + @Override + @SuppressWarnings("unchecked") + public Q buildLuceneGreaterThan(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + if (value instanceof Comparable) + { + Comparable comparable = (Comparable) value; + if (comparable.compareTo(value) > 0) + { + return lqpa.getMatchAllQuery(); + } + else + { + return lqpa.getMatchNoneQuery(); + } + } + else + { + return lqpa.getMatchNoneQuery(); + } + } + + @Override + @SuppressWarnings("unchecked") + public Q buildLuceneGreaterThanOrEquals(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + if (value instanceof Comparable) + { + Comparable comparable = (Comparable) value; + if (comparable.compareTo(value) >= 0) + { + return lqpa.getMatchAllQuery(); + } + else + { + return lqpa.getMatchNoneQuery(); + } + } + else + { + return lqpa.getMatchNoneQuery(); + } + } + + @Override + public Q buildLuceneIn(LuceneQueryParserAdaptor lqpa, Collection values, Boolean not, PredicateMode mode) throws E + { + boolean in = false; + for (Serializable value : values) + { + if (EqualsHelper.nullSafeEquals(value, value)) + { + in = true; + break; + } + } + + if (in == !not) + { + return lqpa.getMatchAllQuery(); + } + else + { + return lqpa.getMatchNoneQuery(); + } + } + + @Override + public Q buildLuceneInequality(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + if (!EqualsHelper.nullSafeEquals(value, value)) + { + return lqpa.getMatchAllQuery(); + } + else + { + return lqpa.getMatchNoneQuery(); + } + } + + @Override + @SuppressWarnings("unchecked") + public Q buildLuceneLessThan(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + if (value instanceof Comparable) + { + Comparable comparable = (Comparable) value; + if (comparable.compareTo(value) < 0) + { + return lqpa.getMatchAllQuery(); } + else + { + return lqpa.getMatchNoneQuery(); + } + } + else + { + return lqpa.getMatchNoneQuery(); + } + } + + @Override + @SuppressWarnings("unchecked") + public Q buildLuceneLessThanOrEquals(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + if (value instanceof Comparable) + { + Comparable comparable = (Comparable) value; + if (comparable.compareTo(value) <= 0) + { + return lqpa.getMatchAllQuery(); + } + else + { + return lqpa.getMatchNoneQuery(); + } + } + else + { + return lqpa.getMatchNoneQuery(); + } + } + + @Override + public Q buildLuceneLike(LuceneQueryParserAdaptor lqpa, Serializable value, Boolean not) throws E + { + if (value != null) + { + boolean matches = false; + + Object converted = DefaultTypeConverter.INSTANCE.convert(value.getClass(), value); + String asString = DefaultTypeConverter.INSTANCE.convert(String.class, converted); + String regExpression = SearchLanguageConversion.convertSQLLikeToRegex(asString); + Pattern pattern = Pattern.compile(regExpression); + String target = DefaultTypeConverter.INSTANCE.convert(String.class, value); + Matcher matcher = pattern.matcher(target); + if (matcher.matches()) + { + matches = true; + } + + if (matches == !not) + { + return lqpa.getMatchAllQuery(); + } + else + { + return lqpa.getMatchNoneQuery(); + } + } + else + { + return lqpa.getMatchNoneQuery(); + } + } + + @Override + public String getLuceneSortField(LuceneQueryParserAdaptor lqpa) + { + throw new UnsupportedOperationException(); + } + + @Override + public String getLuceneFieldName() + { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/main/java/org/alfresco/opencmis/mapping/NotSupportedLuceneBuilder.java b/src/main/java/org/alfresco/opencmis/mapping/NotSupportedLuceneBuilder.java new file mode 100644 index 0000000..323f551 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/mapping/NotSupportedLuceneBuilder.java @@ -0,0 +1,42 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.mapping; + + +/** + * Lucene Builder for properties which do not support query + * + * @author davidc + */ +public class NotSupportedLuceneBuilder extends BaseLuceneBuilder +{ + /** + * Construct + */ + public NotSupportedLuceneBuilder() + { + } +} diff --git a/src/main/java/org/alfresco/opencmis/mapping/ObjectIdLuceneBuilder.java b/src/main/java/org/alfresco/opencmis/mapping/ObjectIdLuceneBuilder.java new file mode 100644 index 0000000..487fd19 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/mapping/ObjectIdLuceneBuilder.java @@ -0,0 +1,168 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.mapping; + +import java.io.Serializable; +import java.util.ArrayList; + +import org.alfresco.repo.search.adaptor.lucene.AnalysisMode; +import org.alfresco.repo.search.adaptor.lucene.LuceneFunction; +import org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor; +import org.alfresco.repo.search.impl.querymodel.PredicateMode; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.apache.chemistry.opencmis.commons.PropertyIds; +import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; + +/** + * Lucene Builder for CMIS object id property. + * + * @author andyh + * @author dward + */ +public class ObjectIdLuceneBuilder extends BaseLuceneBuilder +{ + private DictionaryService dictionaryService; + + /** + * Construct + * + * @param dictionaryService DictionaryService + */ + public ObjectIdLuceneBuilder(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + @Override + public String getLuceneFieldName() + { + return "ID"; + } + + private StoreRef getStore(LuceneQueryParserAdaptor lqpa) + { + ArrayList stores = lqpa.getSearchParameters().getStores(); + if(stores.size() < 1) + { + // default + return StoreRef.STORE_REF_WORKSPACE_SPACESSTORE; + } + return stores.get(0); + } + + private String getValueAsString(LuceneQueryParserAdaptor lqpa, Serializable value) + { + String nodeRefStr = null; + if(!NodeRef.isNodeRef((String)value)) + { + // assume the object id is the node guid + StoreRef storeRef = getStore(lqpa); + nodeRefStr = storeRef.toString() + "/" + (String)value; + } + else + { + nodeRefStr = (String)value; + } + + Object converted = DefaultTypeConverter.INSTANCE.convert(dictionaryService.getDataType(DataTypeDefinition.NODE_REF), nodeRefStr); + String asString = DefaultTypeConverter.INSTANCE.convert(String.class, converted); + return asString; + } + + @Override + public Q buildLuceneEquality(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, LuceneFunction luceneFunction) throws E + { + String field = getLuceneFieldName(); + String stringValue = getValueAsString(lqpa, value); + return lqpa.getIdentifierQuery(field, stringValue, AnalysisMode.IDENTIFIER, luceneFunction); + } + + @Override + public Q buildLuceneExists(LuceneQueryParserAdaptor lqpa, Boolean not) throws E + { + if (not) + { + return lqpa.getMatchNoneQuery(); + } else + { + return lqpa.getMatchAllQuery(); + } + } + + @Override + public Q buildLuceneGreaterThan(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, + LuceneFunction luceneFunction) throws E + { + throw new CmisInvalidArgumentException("Property " + PropertyIds.OBJECT_ID + " can not be used in a 'greater than' comparison"); + } + + @Override + public Q buildLuceneGreaterThanOrEquals(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, + LuceneFunction luceneFunction) throws E + { + throw new CmisInvalidArgumentException("Property " + PropertyIds.OBJECT_ID + + " can not be used in a 'greater than or equals' comparison"); + } + + @Override + public Q buildLuceneLessThan(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, + LuceneFunction luceneFunction) throws E + { + throw new CmisInvalidArgumentException("Property " + PropertyIds.OBJECT_ID + " can not be used in a 'less than' comparison"); + } + + @Override + public Q buildLuceneLessThanOrEquals(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, + LuceneFunction luceneFunction) throws E + { + throw new CmisInvalidArgumentException("Property " + PropertyIds.OBJECT_ID + " can not be used in a 'less than or equals' comparison"); + } + + @Override + public Q buildLuceneLike(LuceneQueryParserAdaptor lqpa, Serializable value, Boolean not) throws E + { + String field = getLuceneFieldName(); + String stringValue = getValueAsString(lqpa, value); + + Q q = lqpa.getIdentifieLikeQuery(field, stringValue, AnalysisMode.IDENTIFIER); + if(not) + { + q = lqpa.getNegatedQuery(q); + } + return q; + } + + @Override + public String getLuceneSortField(LuceneQueryParserAdaptor lqpa) + { + return getLuceneFieldName(); + } + +} diff --git a/src/main/java/org/alfresco/opencmis/mapping/ObjectTypeIdLuceneBuilder.java b/src/main/java/org/alfresco/opencmis/mapping/ObjectTypeIdLuceneBuilder.java new file mode 100644 index 0000000..41696c2 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/mapping/ObjectTypeIdLuceneBuilder.java @@ -0,0 +1,154 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.mapping; + +import java.io.Serializable; + +import org.alfresco.opencmis.dictionary.CMISDictionaryService; +import org.alfresco.opencmis.dictionary.TypeDefinitionWrapper; +import org.alfresco.repo.search.adaptor.lucene.AnalysisMode; +import org.alfresco.repo.search.adaptor.lucene.LuceneFunction; +import org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor; +import org.alfresco.repo.search.impl.querymodel.PredicateMode; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.apache.chemistry.opencmis.commons.PropertyIds; +import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; + +/** + * Lucene Builder for CMIS object type id property + * + * @author andyh + */ +public class ObjectTypeIdLuceneBuilder extends BaseLuceneBuilder +{ + private CMISDictionaryService cmisDictionaryService; + + /** + * Construct + * + * @param cmisDictionaryService CMISDictionaryService + */ + public ObjectTypeIdLuceneBuilder(CMISDictionaryService cmisDictionaryService) + { + this.cmisDictionaryService = cmisDictionaryService; + } + + @Override + public String getLuceneFieldName() + { + return "EXACTTYPE"; + } + + private String getValueAsString(Serializable value) + { + // Object converted = + // DefaultTypeConverter.INSTANCE.convert(getServiceRegistry().getDictionaryService().getDataType(DataTypeDefinition.QNAME), + // value); + String asString = DefaultTypeConverter.INSTANCE.convert(String.class, value); + return asString; + } + + @Override + public Q buildLuceneEquality(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, + LuceneFunction luceneFunction) throws E + { + String field = getLuceneFieldName(); + String stringValue = getValueAsString(value); + TypeDefinitionWrapper type = cmisDictionaryService.findType(stringValue); + return lqpa + .getFieldQuery(field, type.getAlfrescoClass().toString(), AnalysisMode.IDENTIFIER, luceneFunction); + } + + @Override + public Q buildLuceneExists(LuceneQueryParserAdaptor lqpa, Boolean not) throws E + { + if (not) + { + return lqpa.getMatchNoneQuery(); + } else + { + return lqpa.getMatchAllQuery(); + } + } + + @Override + public Q buildLuceneGreaterThan(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, + LuceneFunction luceneFunction) throws E + { + throw new CmisInvalidArgumentException("Property " + PropertyIds.OBJECT_TYPE_ID + " can not be used in a 'greater than' comparison"); + } + + @Override + public Q buildLuceneGreaterThanOrEquals(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, + LuceneFunction luceneFunction) throws E + { + throw new CmisInvalidArgumentException("Property " + PropertyIds.OBJECT_TYPE_ID + + " can not be used in a 'greater than or equals' comparison"); + } + + @Override + public Q buildLuceneInequality(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, + LuceneFunction luceneFunction) throws E + { + return lqpa.getNegatedQuery(buildLuceneEquality(lqpa, value, mode, luceneFunction)); + } + + @Override + public Q buildLuceneLessThan(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, + LuceneFunction luceneFunction) throws E + { + throw new CmisInvalidArgumentException("Property " + PropertyIds.OBJECT_TYPE_ID + " can not be used in a 'less than' comparison"); + } + + @Override + public Q buildLuceneLessThanOrEquals(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, + LuceneFunction luceneFunction) throws E + { + throw new CmisInvalidArgumentException("Property " + PropertyIds.OBJECT_TYPE_ID + " can not be used in a 'less than or equals' comparison"); + } + + @Override + public Q buildLuceneLike(LuceneQueryParserAdaptor lqpa, Serializable value, Boolean not) throws E + { + String field = getLuceneFieldName(); + String stringValue = getValueAsString(value); + TypeDefinitionWrapper type = cmisDictionaryService.findType(stringValue); + String typeQName = type.getAlfrescoClass().toString(); + + Q q = lqpa.getLikeQuery(field, typeQName, AnalysisMode.IDENTIFIER); + if (not) + { + q = lqpa.getNegatedQuery(q); + } + return q; + } + + @Override + public String getLuceneSortField(LuceneQueryParserAdaptor lqpa) + { + return getLuceneFieldName(); + } +} diff --git a/src/main/java/org/alfresco/opencmis/mapping/ParentLuceneBuilder.java b/src/main/java/org/alfresco/opencmis/mapping/ParentLuceneBuilder.java new file mode 100644 index 0000000..31184f2 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/mapping/ParentLuceneBuilder.java @@ -0,0 +1,135 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.mapping; + +import java.io.Serializable; +import java.util.ArrayList; + +import org.alfresco.repo.search.adaptor.lucene.AnalysisMode; +import org.alfresco.repo.search.adaptor.lucene.LuceneFunction; +import org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor; +import org.alfresco.repo.search.impl.querymodel.PredicateMode; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; + + +/** + * Lucene Builder for CMIS parent property + * + * @author andyh + * + */ +public class ParentLuceneBuilder extends BaseLuceneBuilder +{ + private DictionaryService dictionaryService; + + /** + * Construct + * + * @param dictionaryService DictionaryService + */ + public ParentLuceneBuilder(DictionaryService dictionaryService) + { + super(); + this.dictionaryService = dictionaryService; + } + + private StoreRef getStore(LuceneQueryParserAdaptor lqpa) + { + ArrayList stores = lqpa.getSearchParameters().getStores(); + if(stores.size() < 1) + { + // default + return StoreRef.STORE_REF_WORKSPACE_SPACESSTORE; + } + return stores.get(0); + } + + @Override + public String getLuceneFieldName() + { + return "PARENT"; + } + + private String getValueAsString(LuceneQueryParserAdaptor lqpa, Serializable value) + { + String nodeRefStr = (String)value; + if(!NodeRef.isNodeRef((String)value)) + { + // assume the value (object id) is the node guid + StoreRef storeRef = getStore(lqpa); + nodeRefStr = storeRef.toString() + "/" + (String)value; + } + + Object converted = DefaultTypeConverter.INSTANCE.convert(dictionaryService.getDataType(DataTypeDefinition.NODE_REF), nodeRefStr); + String asString = DefaultTypeConverter.INSTANCE.convert(String.class, converted); + return asString; + } + + @Override + public Q buildLuceneEquality(LuceneQueryParserAdaptor lqpa, Serializable value, PredicateMode mode, + LuceneFunction luceneFunction) throws E + { + String field = getLuceneFieldName(); + String stringValue = getValueAsString(lqpa, value); + return lqpa.getFieldQuery(field, stringValue, AnalysisMode.IDENTIFIER, luceneFunction); + } + + @Override + public Q buildLuceneExists(LuceneQueryParserAdaptor lqpa, Boolean not) throws E + { + if (not) + { + return lqpa.getFieldQuery("ISROOT", "T", AnalysisMode.IDENTIFIER, LuceneFunction.FIELD); + } else + { + return lqpa.getNegatedQuery(lqpa.getFieldQuery("ISROOT", "T", AnalysisMode.IDENTIFIER, LuceneFunction.FIELD)); + } + } + + @Override + public Q buildLuceneLike(LuceneQueryParserAdaptor lqpa, Serializable value, Boolean not) throws E + { + String field = getLuceneFieldName(); + String stringValue = getValueAsString(lqpa, value); + + Q q = lqpa.getLikeQuery(field, stringValue, AnalysisMode.IDENTIFIER); + if (not) + { + return lqpa.getNegatedQuery(q); + } + return q; + } + + @Override + public String getLuceneSortField(LuceneQueryParserAdaptor lqpa) + { + return getLuceneFieldName(); + } +} diff --git a/src/main/java/org/alfresco/opencmis/mapping/RuntimePropertyLuceneBuilderMapping.java b/src/main/java/org/alfresco/opencmis/mapping/RuntimePropertyLuceneBuilderMapping.java new file mode 100644 index 0000000..c9d610a --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/mapping/RuntimePropertyLuceneBuilderMapping.java @@ -0,0 +1,127 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.mapping; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.opencmis.dictionary.CMISDictionaryService; +import org.alfresco.opencmis.dictionary.CMISPropertyLuceneBuilder; +import org.alfresco.opencmis.dictionary.PropertyLuceneBuilderMapping; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.opencmis.commons.PropertyIds; +import org.springframework.beans.factory.InitializingBean; + + +/** + * Lucene Builder mappings for built-in CMIS properties + * + * @author davidc + */ +public class RuntimePropertyLuceneBuilderMapping implements PropertyLuceneBuilderMapping, InitializingBean +{ + private CMISDictionaryService cmisDictionaryService; + private DictionaryService dictionaryService; + + private Map luceneBuilders = new HashMap(); + + /** + * @param cmisDictionaryService cmisDictionaryService + */ + public void setCmisDictionaryService(CMISDictionaryService cmisDictionaryService) + { + this.cmisDictionaryService = cmisDictionaryService; + } + + /** + * @param dictionaryService dictionaryService + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + @Override + public void afterPropertiesSet() + { + registerPropertyLuceneBuilder(PropertyIds.OBJECT_ID, new ObjectIdLuceneBuilder(dictionaryService)); + registerPropertyLuceneBuilder(PropertyIds.OBJECT_TYPE_ID, new ObjectTypeIdLuceneBuilder(cmisDictionaryService)); + registerPropertyLuceneBuilder(PropertyIds.BASE_TYPE_ID, new BaseTypeIdLuceneBuilder(cmisDictionaryService)); + registerPropertyLuceneBuilder(PropertyIds.CREATED_BY, new DirectLuceneBuilder(dictionaryService, ContentModel.PROP_CREATOR)); + registerPropertyLuceneBuilder(PropertyIds.CREATION_DATE, new DirectLuceneBuilder(dictionaryService, ContentModel.PROP_CREATED)); + registerPropertyLuceneBuilder(PropertyIds.LAST_MODIFIED_BY, new DirectLuceneBuilder(dictionaryService, ContentModel.PROP_MODIFIER)); + registerPropertyLuceneBuilder(PropertyIds.LAST_MODIFICATION_DATE, new DirectLuceneBuilder(dictionaryService, ContentModel.PROP_MODIFIED)); + registerPropertyLuceneBuilder(PropertyIds.CHANGE_TOKEN, new FixedValueLuceneBuilder(null)); + registerPropertyLuceneBuilder(PropertyIds.NAME, new DirectLuceneBuilder(dictionaryService, ContentModel.PROP_NAME)); + registerPropertyLuceneBuilder(PropertyIds.DESCRIPTION, new DirectLuceneBuilder(dictionaryService, ContentModel.PROP_DESCRIPTION)); + registerPropertyLuceneBuilder(PropertyIds.IS_IMMUTABLE, new NotSupportedLuceneBuilder()); + registerPropertyLuceneBuilder(PropertyIds.IS_LATEST_VERSION, new NotSupportedLuceneBuilder()); + registerPropertyLuceneBuilder(PropertyIds.IS_MAJOR_VERSION, new NotSupportedLuceneBuilder()); + registerPropertyLuceneBuilder(PropertyIds.IS_LATEST_MAJOR_VERSION, new NotSupportedLuceneBuilder()); + registerPropertyLuceneBuilder(PropertyIds.VERSION_LABEL, new NotSupportedLuceneBuilder()); + registerPropertyLuceneBuilder(PropertyIds.VERSION_SERIES_ID, new NotSupportedLuceneBuilder()); + registerPropertyLuceneBuilder(PropertyIds.IS_VERSION_SERIES_CHECKED_OUT, new NotSupportedLuceneBuilder()); + registerPropertyLuceneBuilder(PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, new NotSupportedLuceneBuilder()); + registerPropertyLuceneBuilder(PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, new NotSupportedLuceneBuilder()); + registerPropertyLuceneBuilder(PropertyIds.CHECKIN_COMMENT, new NotSupportedLuceneBuilder()); + registerPropertyLuceneBuilder(PropertyIds.CONTENT_STREAM_LENGTH, new ContentStreamLengthLuceneBuilder(dictionaryService)); + registerPropertyLuceneBuilder(PropertyIds.CONTENT_STREAM_MIME_TYPE, new ContentStreamMimetypeLuceneBuilder(dictionaryService)); + registerPropertyLuceneBuilder(PropertyIds.CONTENT_STREAM_ID, new NotSupportedLuceneBuilder()); + registerPropertyLuceneBuilder(PropertyIds.CONTENT_STREAM_FILE_NAME, new DirectLuceneBuilder(dictionaryService, ContentModel.PROP_NAME)); + registerPropertyLuceneBuilder(PropertyIds.PARENT_ID, new ParentLuceneBuilder(dictionaryService)); + registerPropertyLuceneBuilder(PropertyIds.PATH, new NotSupportedLuceneBuilder()); + registerPropertyLuceneBuilder(PropertyIds.ALLOWED_CHILD_OBJECT_TYPE_IDS, new NotSupportedLuceneBuilder()); + registerPropertyLuceneBuilder(PropertyIds.SOURCE_ID, new NotSupportedLuceneBuilder()); + registerPropertyLuceneBuilder(PropertyIds.TARGET_ID, new NotSupportedLuceneBuilder()); + + registerPropertyLuceneBuilder("alfcmis:nodeRef", new NotSupportedLuceneBuilder()); + } + + @Override + public CMISPropertyLuceneBuilder getPropertyLuceneBuilder(String propertyId) + { + return luceneBuilders.get(propertyId); + } + + @Override + public CMISPropertyLuceneBuilder createDirectPropertyLuceneBuilder(QName propertyName) + { + return new DirectLuceneBuilder(dictionaryService, propertyName); + } + + /** + * Register pre-defined Property Accessor + * + * @param name String + * @param luceneBuilder CMISPropertyLuceneBuilder + */ + private void registerPropertyLuceneBuilder(String name, CMISPropertyLuceneBuilder luceneBuilder) + { + luceneBuilders.put(name, luceneBuilder); + } +} diff --git a/src/main/java/org/alfresco/opencmis/search/CMISFTSQueryParser.java b/src/main/java/org/alfresco/opencmis/search/CMISFTSQueryParser.java new file mode 100644 index 0000000..9751abe --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/search/CMISFTSQueryParser.java @@ -0,0 +1,424 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.search; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.search.adaptor.lucene.AnalysisMode; +import org.alfresco.repo.search.impl.parsers.CMIS_FTSLexer; +import org.alfresco.repo.search.impl.parsers.CMIS_FTSParser; +import org.alfresco.repo.search.impl.parsers.FTSQueryException; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.Column; +import org.alfresco.repo.search.impl.querymodel.Constraint; +import org.alfresco.repo.search.impl.querymodel.Constraint.Occur; +import org.alfresco.repo.search.impl.querymodel.Function; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.LiteralArgument; +import org.alfresco.repo.search.impl.querymodel.QueryModelFactory; +import org.alfresco.repo.search.impl.querymodel.QueryOptions.Connective; +import org.alfresco.repo.search.impl.querymodel.Selector; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSPhrase; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSTerm; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.CharStream; +import org.antlr.runtime.CommonTokenStream; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.Token; +import org.antlr.runtime.tree.CommonTree; +import org.antlr.runtime.tree.Tree; + +public class CMISFTSQueryParser +{ + @SuppressWarnings("unused") + static public Constraint buildFTS(String ftsExpression, QueryModelFactory factory, FunctionEvaluationContext functionEvaluationContext, Selector selector, + Map columnMap, String defaultField) + { + // TODO: Decode sql escape for '' should do in CMIS layer + + // parse templates to trees ... + + CMIS_FTSParser parser = null; + try + { + CharStream cs = new ANTLRStringStream(ftsExpression); + CMIS_FTSLexer lexer = new CMIS_FTSLexer(cs); + CommonTokenStream tokens = new CommonTokenStream(lexer); + parser = new CMIS_FTSParser(tokens); + CommonTree ftsNode = (CommonTree) parser.cmisFtsQuery().getTree(); + return buildFTSConnective(ftsNode, factory, functionEvaluationContext, selector, columnMap, defaultField); + } + catch (RecognitionException e) + { + if (parser != null) + { + String[] tokenNames = parser.getTokenNames(); + String hdr = parser.getErrorHeader(e); + String msg = parser.getErrorMessage(e, tokenNames); + throw new FTSQueryException(hdr + "\n" + msg, e); + } + return null; + } + + } + + static private Constraint buildFTSConnective(CommonTree node, QueryModelFactory factory, FunctionEvaluationContext functionEvaluationContext, + Selector selector, Map columnMap, String defaultField) + { + Connective connective; + switch (node.getType()) + { + case CMIS_FTSParser.DISJUNCTION: + connective = Connective.OR; + break; + case CMIS_FTSParser.CONJUNCTION: + connective = Connective.AND; + break; + default: + throw new FTSQueryException("Invalid connective ..." + node.getText()); + } + + List constraints = new ArrayList(node.getChildCount()); + CommonTree testNode; + for (int i = 0; i < node.getChildCount(); i++) + { + CommonTree subNode = (CommonTree) node.getChild(i); + Constraint constraint; + switch (subNode.getType()) + { + case CMIS_FTSParser.DISJUNCTION: + case CMIS_FTSParser.CONJUNCTION: + constraint = buildFTSConnective(subNode, factory, functionEvaluationContext, selector, columnMap, defaultField); + break; + case CMIS_FTSParser.DEFAULT: + testNode = (CommonTree) subNode.getChild(0); + constraint = buildFTSTest(testNode, factory, functionEvaluationContext, selector, columnMap, defaultField); + constraint.setOccur(Occur.DEFAULT); + break; + case CMIS_FTSParser.EXCLUDE: + testNode = (CommonTree) subNode.getChild(0); + constraint = buildFTSTest(testNode, factory, functionEvaluationContext, selector, columnMap, defaultField); + constraint.setOccur(Occur.EXCLUDE); + break; + + default: + throw new FTSQueryException("Unsupported FTS option " + subNode.getText()); + } + constraints.add(constraint); + } + if (constraints.size() == 1) + { + return constraints.get(0); + } + else + { + if (connective == Connective.OR) + { + return factory.createDisjunction(constraints); + } + else + { + return factory.createConjunction(constraints); + } + } + } + + static private Constraint buildFTSTest(CommonTree argNode, QueryModelFactory factory, FunctionEvaluationContext functionEvaluationContext, + Selector selector, Map columnMap, String defaultField) + { + CommonTree testNode = argNode; + switch (testNode.getType()) + { + case CMIS_FTSParser.DISJUNCTION: + case CMIS_FTSParser.CONJUNCTION: + return buildFTSConnective(testNode, factory, functionEvaluationContext, selector, columnMap, defaultField); + case CMIS_FTSParser.TERM: + return buildTerm(testNode, factory, functionEvaluationContext, selector, columnMap); + case CMIS_FTSParser.PHRASE: + return buildPhrase(testNode, factory, functionEvaluationContext, selector, columnMap); + default: + throw new FTSQueryException("Unsupported FTS option " + testNode.getText()); + } + } + + static private Constraint buildPhrase(CommonTree testNode, QueryModelFactory factory, + FunctionEvaluationContext functionEvaluationContext, Selector selector, Map columnMap) + { + String functionName = FTSPhrase.NAME; + Function function = factory.getFunction(functionName); + Map functionArguments = new LinkedHashMap(); + LiteralArgument larg = factory.createLiteralArgument(FTSPhrase.ARG_PHRASE, DataTypeDefinition.TEXT, getText(testNode.getChild(0))); + functionArguments.put(larg.getName(), larg); + larg = factory.createLiteralArgument(FTSPhrase.ARG_TOKENISATION_MODE, DataTypeDefinition.ANY, AnalysisMode.DEFAULT); + functionArguments.put(larg.getName(), larg); + return factory.createFunctionalConstraint(function, functionArguments); + } + + static private Constraint buildTerm(CommonTree testNode, QueryModelFactory factory, FunctionEvaluationContext functionEvaluationContext, + Selector selector, Map columnMap) + { + String functionName = FTSTerm.NAME; + Function function = factory.getFunction(functionName); + Map functionArguments = new LinkedHashMap(); + LiteralArgument larg = factory.createLiteralArgument(FTSTerm.ARG_TERM, DataTypeDefinition.TEXT, getText(testNode.getChild(0))); + functionArguments.put(larg.getName(), larg); + larg = factory.createLiteralArgument(FTSTerm.ARG_TOKENISATION_MODE, DataTypeDefinition.ANY, AnalysisMode.DEFAULT); + functionArguments.put(larg.getName(), larg); + return factory.createFunctionalConstraint(function, functionArguments); + } + + + static class DisjunctionToken implements Token + { + + public int getChannel() + { + return 0; + } + + public int getCharPositionInLine() + { + return 0; + } + + public CharStream getInputStream() + { + return null; + } + + public int getLine() + { + return 0; + } + + public String getText() + { + return null; + } + + public int getTokenIndex() + { + return 0; + } + + public int getType() + { + return CMIS_FTSParser.DISJUNCTION; + } + + public void setChannel(int arg0) + { + + + } + + public void setCharPositionInLine(int arg0) + { + + + } + + public void setInputStream(CharStream arg0) + { + + } + + public void setLine(int arg0) + { + + + } + + public void setText(String arg0) + { + + + } + + public void setTokenIndex(int arg0) + { + + } + + public void setType(int arg0) + { + + } + + } + + static class DefaultToken implements Token + { + + public int getChannel() + { + + return 0; + } + + public int getCharPositionInLine() + { + return 0; + } + + public CharStream getInputStream() + { + return null; + } + + public int getLine() + { + return 0; + } + + public String getText() + { + return null; + } + + public int getTokenIndex() + { + return 0; + } + + public int getType() + { + return CMIS_FTSParser.DEFAULT; + } + + public void setChannel(int arg0) + { + + } + + public void setCharPositionInLine(int arg0) + { + + } + + public void setInputStream(CharStream arg0) + { + + } + + public void setLine(int arg0) + { + + } + + public void setText(String arg0) + { + + } + + public void setTokenIndex(int arg0) + { + + } + + public void setType(int arg0) + { + + } + + } + + static private String getText(Tree node) + { + String text = node.getText(); + int index; + switch (node.getType()) + { + case CMIS_FTSParser.FTSWORD: + index = text.indexOf('\\'); + if (index == -1) + { + return text; + } + else + { + return unescape(text); + } + case CMIS_FTSParser.FTSPHRASE: + String phrase = text.substring(1, text.length() - 1); + index = phrase.indexOf('\\'); + if (index == -1) + { + return phrase; + } + else + { + return unescape(phrase); + } + default: + return text; + } + } + + static private String unescape(String string) + { + StringBuilder builder = new StringBuilder(string.length()); + boolean lastWasEscape = false; + + for (int i = 0; i < string.length(); i++) + { + char c = string.charAt(i); + if (lastWasEscape) + { + if (c == 'u') + { + throw new UnsupportedOperationException(string); + } + else + { + builder.append(c); + } + lastWasEscape = false; + } + else + { + if (c == '\\') + { + lastWasEscape = true; + } + else + { + builder.append(c); + } + } + } + if (lastWasEscape) + { + throw new FTSQueryException("Escape character at end of string " + string); + } + + return builder.toString(); + } +} diff --git a/src/main/java/org/alfresco/opencmis/search/CMISQueryOptions.java b/src/main/java/org/alfresco/opencmis/search/CMISQueryOptions.java new file mode 100644 index 0000000..9556647 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/search/CMISQueryOptions.java @@ -0,0 +1,193 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.search; + +import java.util.Locale; + +import org.alfresco.repo.search.impl.querymodel.QueryOptions; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.LimitBy; +import org.alfresco.service.cmr.search.QueryParameterDefinition; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchService; +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * The options for a CMIS query + * + * @author andyh + */ +public class CMISQueryOptions extends QueryOptions +{ + public enum CMISQueryMode + { + CMS_STRICT, CMS_WITH_ALFRESCO_EXTENSIONS; + } + + private CMISQueryMode queryMode = CMISQueryMode.CMS_STRICT; + private CmisVersion cmisVersion = CmisVersion.CMIS_1_1; + + public static CMISQueryOptions create(SearchParameters searchParameters) + { + String sql = searchParameters.getQuery(); + + CMISQueryOptions options = new CMISQueryOptions(sql, searchParameters.getStores().get(0)); + options.setIncludeInTransactionData(!searchParameters.excludeDataInTheCurrentTransaction()); + options.setDefaultFTSConnective(searchParameters.getDefaultOperator() == SearchParameters.Operator.OR ? Connective.OR : Connective.AND); + options.setDefaultFTSFieldConnective(searchParameters.getDefaultOperator() == SearchParameters.Operator.OR ? Connective.OR : Connective.AND); + options.setSkipCount(searchParameters.getSkipCount()); + options.setMaxPermissionChecks(searchParameters.getMaxPermissionChecks()); + options.setMaxPermissionCheckTimeMillis(searchParameters.getMaxPermissionCheckTimeMillis()); + if (searchParameters.getLimitBy() == LimitBy.FINAL_SIZE) + { + options.setMaxItems(searchParameters.getLimit()); + } + else + { + options.setMaxItems(searchParameters.getMaxItems()); + } + options.setMlAnalaysisMode(searchParameters.getMlAnalaysisMode()); + options.setLocales(searchParameters.getLocales()); + options.setStores(searchParameters.getStores()); + options.setUseInMemorySort(searchParameters.getUseInMemorySort()); + options.setMaxRawResultSetSizeForInMemorySort(searchParameters.getMaxRawResultSetSizeForInMemorySort()); + //options.setQuery(); Done on conbstruction + //options.setQueryMode(); Should set afterwards + options.setQueryParameterDefinitions(searchParameters.getQueryParameterDefinitions()); + options.setDefaultFieldName(searchParameters.getDefaultFieldName()); + options.setBulkFetchEnabled(searchParameters.isBulkFetchEnabled()); + options.setExcludeTenantFilter(searchParameters.getExcludeTenantFilter()); + options.setSinceTxId(searchParameters.getSinceTxId()); + return options; + } + + /** + * Create a CMISQueryOptions instance with the default options other than + * the query and store ref. The query will be run using the locale returned + * by I18NUtil.getLocale() + * + * @param query + * - the query to run + * @param storeRef + * - the store against which to run the query + */ + public CMISQueryOptions(String query, StoreRef storeRef) + { + this(query, storeRef, I18NUtil.getLocale()); + } + + /** + * Create a CMISQueryOptions instance with the default options other than + * the query, store ref and locale. + * + * @param query + * - the query to run + * @param storeRef + * - the store against which to run the query + */ + public CMISQueryOptions(String query, StoreRef storeRef, Locale locale) + { + super(query, storeRef, locale); + } + + public CmisVersion getCmisVersion() + { + return cmisVersion; + } + + public void setCmisVersion(CmisVersion cmisVersion) + { + this.cmisVersion = cmisVersion; + } + + /** + * Get the query mode. + * + * @return the queryMode + */ + public CMISQueryMode getQueryMode() + { + return queryMode; + } + + /** + * Set the query mode. + * + * @param queryMode + * the queryMode to set + */ + public void setQueryMode(CMISQueryMode queryMode) + { + this.queryMode = queryMode; + } + + /** + * @return SearchParameters + */ + public SearchParameters getAsSearchParmeters() + { + SearchParameters searchParameters = new SearchParameters(); + searchParameters.setDefaultFieldName(this.getDefaultFieldName()); + searchParameters.setDefaultFTSFieldConnective(this.getDefaultFTSFieldConnective() == Connective.OR ? SearchParameters.Operator.OR : SearchParameters.Operator.AND); + searchParameters.setDefaultFTSOperator(this.getDefaultFTSConnective() == Connective.OR ? SearchParameters.Operator.OR : SearchParameters.Operator.AND); + searchParameters.setDefaultOperator(this.getDefaultFTSConnective() == Connective.OR ? SearchParameters.Operator.OR : SearchParameters.Operator.AND); + searchParameters.setLanguage(SearchService.LANGUAGE_CMIS_ALFRESCO); + if(this.getMaxItems() > 0) + { + searchParameters.setLimit(this.getMaxItems()); + searchParameters.setLimitBy(LimitBy.FINAL_SIZE); + searchParameters.setMaxItems(this.getMaxItems()); + } + searchParameters.setMaxPermissionChecks(this.getMaxPermissionChecks()); + searchParameters.setMaxPermissionCheckTimeMillis(this.getMaxPermissionCheckTimeMillis()); + searchParameters.setMlAnalaysisMode(this.getMlAnalaysisMode()); + //searchParameters.setNamespace() TODO: Fix + //searchParameters.setPermissionEvaluation() + searchParameters.setQuery(this.getQuery()); + searchParameters.setSkipCount(this.getSkipCount()); + //searchParameters.addAllAttribute() + for(Locale locale : this.getLocales()) + { + searchParameters.addLocale(locale); + } + for(QueryParameterDefinition queryParameterDefinition: this.getQueryParameterDefinitions()) + { + searchParameters.addQueryParameterDefinition(queryParameterDefinition); + } + //searchParameters.addQueryTemplate(name, template) + //searchParameters.addSort() + for(StoreRef storeRef : this.getStores()) + { + searchParameters.addStore(storeRef); + } + //searchParameters.addTextAttribute() + searchParameters.setBulkFetchEnabled(isBulkFetchEnabled()); + searchParameters.setQueryConsistency(this.getQueryConsistency()); + searchParameters.setSinceTxId(getSinceTxId()); + return searchParameters; + } +} diff --git a/src/main/java/org/alfresco/opencmis/search/CMISQueryParser.java b/src/main/java/org/alfresco/opencmis/search/CMISQueryParser.java new file mode 100644 index 0000000..1b219c8 --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/search/CMISQueryParser.java @@ -0,0 +1,1718 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.search; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +import org.alfresco.opencmis.dictionary.CMISDictionaryService; +import org.alfresco.opencmis.dictionary.PropertyDefinitionWrapper; +import org.alfresco.opencmis.dictionary.TypeDefinitionWrapper; +import org.alfresco.opencmis.search.CMISQueryOptions.CMISQueryMode; +import org.alfresco.repo.search.impl.parsers.CMISLexer; +import org.alfresco.repo.search.impl.parsers.CMISParser; +import org.alfresco.repo.search.impl.parsers.FTSParser; +import org.alfresco.repo.search.impl.parsers.FTSQueryException; +import org.alfresco.repo.search.impl.parsers.FTSQueryParser; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.ArgumentDefinition; +import org.alfresco.repo.search.impl.querymodel.Column; +import org.alfresco.repo.search.impl.querymodel.Constraint; +import org.alfresco.repo.search.impl.querymodel.Constraint.Occur; +import org.alfresco.repo.search.impl.querymodel.Function; +import org.alfresco.repo.search.impl.querymodel.FunctionArgument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.JoinType; +import org.alfresco.repo.search.impl.querymodel.ListArgument; +import org.alfresco.repo.search.impl.querymodel.LiteralArgument; +import org.alfresco.repo.search.impl.querymodel.Order; +import org.alfresco.repo.search.impl.querymodel.Ordering; +import org.alfresco.repo.search.impl.querymodel.ParameterArgument; +import org.alfresco.repo.search.impl.querymodel.PredicateMode; +import org.alfresco.repo.search.impl.querymodel.PropertyArgument; +import org.alfresco.repo.search.impl.querymodel.Query; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.QueryModelFactory; +import org.alfresco.repo.search.impl.querymodel.QueryOptions.Connective; +import org.alfresco.repo.search.impl.querymodel.Selector; +import org.alfresco.repo.search.impl.querymodel.SelectorArgument; +import org.alfresco.repo.search.impl.querymodel.Source; +import org.alfresco.repo.search.impl.querymodel.impl.BaseComparison; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Child; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Descendant; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Equals; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Exists; +import org.alfresco.repo.search.impl.querymodel.impl.functions.GreaterThan; +import org.alfresco.repo.search.impl.querymodel.impl.functions.GreaterThanOrEquals; +import org.alfresco.repo.search.impl.querymodel.impl.functions.In; +import org.alfresco.repo.search.impl.querymodel.impl.functions.LessThan; +import org.alfresco.repo.search.impl.querymodel.impl.functions.LessThanOrEquals; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Like; +import org.alfresco.repo.search.impl.querymodel.impl.functions.NotEquals; +import org.alfresco.repo.search.impl.querymodel.impl.functions.PropertyAccessor; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Score; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.CachingDateFormat; +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.CharStream; +import org.antlr.runtime.CommonTokenStream; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.tree.CommonTree; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.enums.CapabilityJoin; +import org.apache.chemistry.opencmis.commons.enums.PropertyType; +import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; + +/** + * @author andyh + */ +public class CMISQueryParser +{ + private enum EscapeMode + { + LITERAL, LIKE, CONTAINS; + } + + private CMISQueryOptions options; + + private CMISDictionaryService cmisDictionaryService; + + private CapabilityJoin joinSupport; + + private BaseTypeId[] validScopes; + + private boolean hasScore = false; + + private boolean hasContains = false; + + public CMISQueryParser(CMISQueryOptions options, CMISDictionaryService cmisDictionaryService, + CapabilityJoin joinSupport) + { + this.options = options; + this.cmisDictionaryService = cmisDictionaryService; + this.joinSupport = joinSupport; + this.validScopes = (options.getQueryMode() == CMISQueryMode.CMS_STRICT) ? CmisFunctionEvaluationContext.STRICT_SCOPES + : CmisFunctionEvaluationContext.ALFRESCO_SCOPES; + } + + @SuppressWarnings("unused") + public Query parse(QueryModelFactory factory, FunctionEvaluationContext functionEvaluationContext) + { + + CMISParser parser = null; + try + { + CharStream cs = new ANTLRStringStream(options.getQuery()); + CMISLexer lexer = new CMISLexer(cs); + CommonTokenStream tokens = new CommonTokenStream(lexer); + parser = new CMISParser(tokens); + parser.setStrict(options.getQueryMode() == CMISQueryMode.CMS_STRICT); + CommonTree queryNode = (CommonTree) parser.query().getTree(); + + CommonTree sourceNode = (CommonTree) queryNode.getFirstChildWithType(CMISParser.SOURCE); + Source source = buildSource(sourceNode, joinSupport, factory); + Map selectors = source.getSelectors(); + ArrayList columns = buildColumns(queryNode, factory, selectors, options.getQuery()); + + HashMap columnMap = new HashMap(); + for (Column column : columns) + { + if (columnMap.containsKey(column.getAlias())) + { + throw new CmisInvalidArgumentException("Duplicate column alias for " + column.getAlias()); + } else + { + columnMap.put(column.getAlias(), column); + } + } + + ArrayList orderings = buildOrderings(queryNode, factory, selectors, columns); + + Constraint constraint = null; + CommonTree orNode = (CommonTree) queryNode.getFirstChildWithType(CMISParser.DISJUNCTION); + if (orNode != null) + { + constraint = buildDisjunction(orNode, factory, functionEvaluationContext, selectors, columnMap); + } + + Query query = factory.createQuery(columns, source, constraint, orderings); + + // TODO: validate query and use of ID, function arguments matching + // up etc + + if (options.getQueryMode() == CMISQueryMode.CMS_STRICT) + { + if (hasScore && !hasContains) + { + throw new CmisInvalidArgumentException("Function SCORE() used without matching CONTAINS() function"); + } + } + + return query; + } catch (RecognitionException e) + { + if (parser != null) + { + String[] tokenNames = parser.getTokenNames(); + String hdr = parser.getErrorHeader(e); + String msg = parser.getErrorMessage(e, tokenNames); + throw new CmisInvalidArgumentException(hdr + "\n" + msg, e); + } + } + throw new CmisInvalidArgumentException("Failed to parse"); + } + + /** + * @param orNode CommonTree + * @param factory QueryModelFactory + * @param functionEvaluationContext FunctionEvaluationContext + * @param selectors Map + * @param columnMap HashMap + * @return Constraint + */ + private Constraint buildDisjunction(CommonTree orNode, QueryModelFactory factory, + FunctionEvaluationContext functionEvaluationContext, Map selectors, + HashMap columnMap) + { + List constraints = new ArrayList(orNode.getChildCount()); + for (int i = 0; i < orNode.getChildCount(); i++) + { + CommonTree andNode = (CommonTree) orNode.getChild(i); + Constraint constraint = buildConjunction(andNode, factory, functionEvaluationContext, selectors, columnMap); + constraints.add(constraint); + } + if (constraints.size() == 1) + { + return constraints.get(0); + } else + { + return factory.createDisjunction(constraints); + } + } + + /** + * @param andNode CommonTree + * @param factory QueryModelFactory + * @param functionEvaluationContext FunctionEvaluationContext + * @param selectors Map + * @param columnMap HashMap + * @return Constraint + */ + private Constraint buildConjunction(CommonTree andNode, QueryModelFactory factory, + FunctionEvaluationContext functionEvaluationContext, Map selectors, + HashMap columnMap) + { + List constraints = new ArrayList(andNode.getChildCount()); + for (int i = 0; i < andNode.getChildCount(); i++) + { + CommonTree notNode = (CommonTree) andNode.getChild(i); + Constraint constraint = buildNegation(notNode, factory, functionEvaluationContext, selectors, columnMap); + constraints.add(constraint); + } + if (constraints.size() == 1 && constraints.get(0).getOccur() != Occur.EXCLUDE) + { + return constraints.get(0); + } else + { + return factory.createConjunction(constraints); + } + } + + /** + * @param notNode CommonTree + * @param factory QueryModelFactory + * @param functionEvaluationContext FunctionEvaluationContext + * @param selectors Map + * @param columnMap HashMap + * @return Constraint + */ + private Constraint buildNegation(CommonTree notNode, QueryModelFactory factory, + FunctionEvaluationContext functionEvaluationContext, Map selectors, + HashMap columnMap) + { + if (notNode.getType() == CMISParser.NEGATION) + { + Constraint constraint = buildTest((CommonTree) notNode.getChild(0), factory, functionEvaluationContext, + selectors, columnMap); + constraint.setOccur(Occur.EXCLUDE); + return constraint; + } else + { + return buildTest(notNode, factory, functionEvaluationContext, selectors, columnMap); + } + } + + /** + * @param testNode CommonTree + * @param factory QueryModelFactory + * @param functionEvaluationContext FunctionEvaluationContext + * @param selectors Map + * @param columnMap HashMap + * @return Constraint + */ + private Constraint buildTest(CommonTree testNode, QueryModelFactory factory, + FunctionEvaluationContext functionEvaluationContext, Map selectors, + HashMap columnMap) + { + if (testNode.getType() == CMISParser.DISJUNCTION) + { + return buildDisjunction(testNode, factory, functionEvaluationContext, selectors, columnMap); + } else + { + return buildPredicate(testNode, factory, functionEvaluationContext, selectors, columnMap); + } + } + + /** + * @param predicateNode CommonTree + * @param factory QueryModelFactory + * @param functionEvaluationContext FunctionEvaluationContext + * @param selectors Map + * @param columnMap HashMap + * @return Constraint + */ + private Constraint buildPredicate(CommonTree predicateNode, QueryModelFactory factory, + FunctionEvaluationContext functionEvaluationContext, Map selectors, + Map columnMap) + { + String functionName; + Function function; + CommonTree argNode; + Map functionArguments; + Argument arg; + switch (predicateNode.getType()) + { + case CMISParser.PRED_CHILD: + functionName = Child.NAME; + function = factory.getFunction(functionName); + functionArguments = new LinkedHashMap(); + argNode = (CommonTree) predicateNode.getChild(0); + arg = getFunctionArgument(argNode, function.getArgumentDefinition(Child.ARG_PARENT), factory, selectors, + columnMap, false); + functionArguments.put(arg.getName(), arg); + if (predicateNode.getChildCount() > 1) + { + argNode = (CommonTree) predicateNode.getChild(1); + arg = getFunctionArgument(argNode, function.getArgumentDefinition(Child.ARG_SELECTOR), factory, + selectors, columnMap, false); + if (!arg.isQueryable()) + { + throw new CmisInvalidArgumentException("The property is not queryable: " + argNode.getText()); + } + functionArguments.put(arg.getName(), arg); + } + return factory.createFunctionalConstraint(function, functionArguments); + case CMISParser.PRED_COMPARISON: + + switch (predicateNode.getChild(2).getType()) + { + case CMISParser.EQUALS: + functionName = Equals.NAME; + function = factory.getFunction(functionName); + break; + case CMISParser.NOTEQUALS: + functionName = NotEquals.NAME; + function = factory.getFunction(functionName); + break; + case CMISParser.GREATERTHAN: + functionName = GreaterThan.NAME; + function = factory.getFunction(functionName); + break; + case CMISParser.GREATERTHANOREQUALS: + functionName = GreaterThanOrEquals.NAME; + function = factory.getFunction(functionName); + break; + case CMISParser.LESSTHAN: + functionName = LessThan.NAME; + function = factory.getFunction(functionName); + break; + case CMISParser.LESSTHANOREQUALS: + functionName = LessThanOrEquals.NAME; + function = factory.getFunction(functionName); + break; + default: + throw new CmisInvalidArgumentException("Unknown comparison function " + + predicateNode.getChild(2).getText()); + } + functionArguments = new LinkedHashMap(); + argNode = (CommonTree) predicateNode.getChild(0); + arg = getFunctionArgument(argNode, function.getArgumentDefinition(BaseComparison.ARG_MODE), factory, + selectors, columnMap, false); + functionArguments.put(arg.getName(), arg); + argNode = (CommonTree) predicateNode.getChild(1); + arg = getFunctionArgument(argNode, function.getArgumentDefinition(BaseComparison.ARG_LHS), factory, + selectors, columnMap, false); + functionArguments.put(arg.getName(), arg); + argNode = (CommonTree) predicateNode.getChild(3); + arg = getFunctionArgument(argNode, function.getArgumentDefinition(BaseComparison.ARG_RHS), factory, + selectors, columnMap, false); + functionArguments.put(arg.getName(), arg); + checkPredicateConditionsForComparisons(function, functionArguments, functionEvaluationContext, columnMap); + return factory.createFunctionalConstraint(function, functionArguments); + case CMISParser.PRED_DESCENDANT: + functionName = Descendant.NAME; + function = factory.getFunction(functionName); + argNode = (CommonTree) predicateNode.getChild(0); + functionArguments = new LinkedHashMap(); + arg = getFunctionArgument(argNode, function.getArgumentDefinition(Descendant.ARG_ANCESTOR), factory, + selectors, columnMap, false); + functionArguments.put(arg.getName(), arg); + if (predicateNode.getChildCount() > 1) + { + argNode = (CommonTree) predicateNode.getChild(1); + arg = getFunctionArgument(argNode, function.getArgumentDefinition(Descendant.ARG_SELECTOR), factory, + selectors, columnMap, false); + functionArguments.put(arg.getName(), arg); + } + return factory.createFunctionalConstraint(function, functionArguments); + case CMISParser.PRED_EXISTS: + functionName = Exists.NAME; + function = factory.getFunction(functionName); + argNode = (CommonTree) predicateNode.getChild(0); + functionArguments = new LinkedHashMap(); + arg = getFunctionArgument(argNode, function.getArgumentDefinition(Exists.ARG_PROPERTY), factory, selectors, + columnMap, false); + functionArguments.put(arg.getName(), arg); + arg = factory.createLiteralArgument(Exists.ARG_NOT, DataTypeDefinition.BOOLEAN, + (predicateNode.getChildCount() > 1)); + functionArguments.put(arg.getName(), arg); + // Applies to both single valued and multi-valued properties - no + // checks required + return factory.createFunctionalConstraint(function, functionArguments); + case CMISParser.PRED_FTS: + if (options.getQueryMode() == CMISQueryMode.CMS_STRICT) + { + if (hasContains) + { + throw new CmisInvalidArgumentException( + "Only one CONTAINS() function can be included in a single query statement."); + } + } + String ftsExpression = predicateNode.getChild(0).getText(); + ftsExpression = ftsExpression.substring(1, ftsExpression.length() - 1); + ftsExpression = unescape(ftsExpression, EscapeMode.CONTAINS); + Selector selector; + if (predicateNode.getChildCount() > 1) + { + String qualifier = predicateNode.getChild(1).getText(); + selector = selectors.get(qualifier); + if (selector == null) + { + throw new CmisInvalidArgumentException("No selector for " + qualifier); + } + } else + { + if (selectors.size() == 1) + { + selector = selectors.get(selectors.keySet().iterator().next()); + } else + { + throw new CmisInvalidArgumentException( + "A selector must be specified when there are two or more selectors"); + } + } + Connective defaultConnective; + Connective defaultFieldConnective; + if (options.getQueryMode() == CMISQueryMode.CMS_STRICT) + { + defaultConnective = Connective.AND; + defaultFieldConnective = Connective.AND; + } else + { + defaultConnective = options.getDefaultFTSConnective(); + defaultFieldConnective = options.getDefaultFTSFieldConnective(); + } + FTSParser.Mode mode; + if (options.getQueryMode() == CMISQueryMode.CMS_STRICT) + { + mode = FTSParser.Mode.CMIS; + } else + { + if (defaultConnective == Connective.AND) + { + mode = FTSParser.Mode.DEFAULT_CONJUNCTION; + } else + { + mode = FTSParser.Mode.DEFAULT_DISJUNCTION; + } + } + Constraint ftsConstraint; + if (options.getQueryMode() == CMISQueryMode.CMS_STRICT) + { + ftsConstraint = CMISFTSQueryParser.buildFTS(ftsExpression, factory, functionEvaluationContext, + selector, columnMap, options.getDefaultFieldName()); + } else + { + ftsConstraint = FTSQueryParser.buildFTS(ftsExpression, factory, functionEvaluationContext, selector, + columnMap, mode, defaultFieldConnective, null, options.getDefaultFieldName(), FTSQueryParser.RerankPhase.SINGLE_PASS); + } + ftsConstraint.setBoost(1000.0f); + hasContains = true; + return ftsConstraint; + case CMISParser.PRED_IN: + functionName = In.NAME; + function = factory.getFunction(functionName); + functionArguments = new LinkedHashMap(); + argNode = (CommonTree) predicateNode.getChild(0); + arg = getFunctionArgument(argNode, function.getArgumentDefinition(In.ARG_MODE), factory, selectors, + columnMap, false); + functionArguments.put(arg.getName(), arg); + argNode = (CommonTree) predicateNode.getChild(1); + arg = getFunctionArgument(argNode, function.getArgumentDefinition(In.ARG_PROPERTY), factory, selectors, + columnMap, false); + functionArguments.put(arg.getName(), arg); + argNode = (CommonTree) predicateNode.getChild(2); + arg = getFunctionArgument(argNode, function.getArgumentDefinition(In.ARG_LIST), factory, selectors, + columnMap, false); + functionArguments.put(arg.getName(), arg); + arg = factory.createLiteralArgument(In.ARG_NOT, DataTypeDefinition.BOOLEAN, + (predicateNode.getChildCount() > 3)); + functionArguments.put(arg.getName(), arg); + checkPredicateConditionsForIn(functionArguments, functionEvaluationContext, columnMap); + return factory.createFunctionalConstraint(function, functionArguments); + case CMISParser.PRED_LIKE: + functionName = Like.NAME; + function = factory.getFunction(functionName); + functionArguments = new LinkedHashMap(); + argNode = (CommonTree) predicateNode.getChild(0); + arg = getFunctionArgument(argNode, function.getArgumentDefinition(Like.ARG_PROPERTY), factory, selectors, + columnMap, false); + functionArguments.put(arg.getName(), arg); + argNode = (CommonTree) predicateNode.getChild(1); + arg = getFunctionArgument(argNode, function.getArgumentDefinition(Like.ARG_EXP), factory, selectors, + columnMap, true); + functionArguments.put(arg.getName(), arg); + arg = factory.createLiteralArgument(Like.ARG_NOT, DataTypeDefinition.BOOLEAN, + (predicateNode.getChildCount() > 2)); + functionArguments.put(arg.getName(), arg); + checkPredicateConditionsForLike(functionArguments, functionEvaluationContext, columnMap); + return factory.createFunctionalConstraint(function, functionArguments); + default: + return null; + } + } + + private void checkPredicateConditionsForIn(Map functionArguments, + FunctionEvaluationContext functionEvaluationContext, Map columnMap) + { + if (options.getQueryMode() == CMISQueryMode.CMS_STRICT) + { + PropertyArgument propertyArgument = (PropertyArgument) functionArguments.get(In.ARG_PROPERTY); + LiteralArgument modeArgument = (LiteralArgument) functionArguments.get(In.ARG_MODE); + String modeString = DefaultTypeConverter.INSTANCE.convert(String.class, + modeArgument.getValue(functionEvaluationContext)); + PredicateMode mode = PredicateMode.valueOf(modeString); + String propertyName = propertyArgument.getPropertyName(); + + Column column = columnMap.get(propertyName); + if (column != null) + { + // check for function type + if (column.getFunction().getName().equals(PropertyAccessor.NAME)) + { + PropertyArgument arg = (PropertyArgument) column.getFunctionArguments().get( + PropertyAccessor.ARG_PROPERTY); + propertyName = arg.getPropertyName(); + } else + { + throw new CmisInvalidArgumentException("Complex column reference not supoprted in LIKE " + + propertyName); + } + } + + boolean isMultiValued = functionEvaluationContext.isMultiValued(propertyName); + + switch (mode) + { + case ANY: + if (isMultiValued) + { + break; + } else + { + throw new QueryModelException("Predicate mode " + PredicateMode.ANY + + " is not supported for IN and single valued properties"); + } + case SINGLE_VALUED_PROPERTY: + if (isMultiValued) + { + throw new QueryModelException("Predicate mode " + PredicateMode.SINGLE_VALUED_PROPERTY + + " is not supported for IN and multi-valued properties"); + } else + { + break; + } + default: + throw new QueryModelException("Unsupported predicate mode " + mode); + } + + PropertyDefinitionWrapper propDef = cmisDictionaryService.findPropertyByQueryName(propertyName); + if (propDef.getPropertyDefinition().getPropertyType() == PropertyType.BOOLEAN) + { + throw new QueryModelException("In is not supported for properties of type Boolean"); + } + } + + } + + private void checkPredicateConditionsForComparisons(Function function, Map functionArguments, + FunctionEvaluationContext functionEvaluationContext, Map columnMap) + { + if (options.getQueryMode() == CMISQueryMode.CMS_STRICT) + { + ((BaseComparison) function).setPropertyAndStaticArguments(functionArguments); + String propertyName = ((BaseComparison) function).getPropertyName(); + LiteralArgument modeArgument = (LiteralArgument) functionArguments.get(BaseComparison.ARG_MODE); + String modeString = DefaultTypeConverter.INSTANCE.convert(String.class, + modeArgument.getValue(functionEvaluationContext)); + PredicateMode mode = PredicateMode.valueOf(modeString); + + Column column = columnMap.get(propertyName); + if (column != null) + { + // check for function type + if (column.getFunction().getName().equals(PropertyAccessor.NAME)) + { + PropertyArgument arg = (PropertyArgument) column.getFunctionArguments().get( + PropertyAccessor.ARG_PROPERTY); + propertyName = arg.getPropertyName(); + } else + { + throw new CmisInvalidArgumentException("Complex column reference not supoprted in LIKE " + + propertyName); + } + } + + boolean isMultiValued = functionEvaluationContext.isMultiValued(propertyName); + + switch (mode) + { + case ANY: + if (isMultiValued) + { + if (function.getName().equals(Equals.NAME)) + { + break; + } else + { + throw new QueryModelException("Predicate mode " + PredicateMode.ANY + " is only supported for " + + Equals.NAME + " (and multi-valued properties)."); + } + } else + { + throw new QueryModelException("Predicate mode " + PredicateMode.ANY + " is not supported for " + + function.getName() + " and single valued properties"); + } + case SINGLE_VALUED_PROPERTY: + if (isMultiValued) + { + throw new QueryModelException("Predicate mode " + PredicateMode.SINGLE_VALUED_PROPERTY + + " is not supported for " + function.getName() + " and multi-valued properties"); + } else + { + break; + } + default: + throw new QueryModelException("Unsupported predicate mode " + mode); + } + + // limit support for ID and Boolean + + PropertyDefinitionWrapper propDef = cmisDictionaryService.findPropertyByQueryName(propertyName); + if (propDef.getPropertyDefinition().getPropertyType() == PropertyType.ID) + { + if (function.getName().equals(Equals.NAME) || function.getName().equals(NotEquals.NAME)) + { + return; + } else + { + throw new QueryModelException("Comparison " + function.getName() + + " is not supported for properties of type ID"); + } + } else if (propDef.getPropertyDefinition().getPropertyType() == PropertyType.BOOLEAN) + { + if (function.getName().equals(Equals.NAME)) + { + return; + } else + { + throw new QueryModelException("Comparison " + function.getName() + + " is not supported for properties of type Boolean"); + } + } + } + + } + + private void checkPredicateConditionsForLike(Map functionArguments, + FunctionEvaluationContext functionEvaluationContext, Map columnMap) + { + if (options.getQueryMode() == CMISQueryMode.CMS_STRICT) + { + PropertyArgument propertyArgument = (PropertyArgument) functionArguments.get(Like.ARG_PROPERTY); + + boolean isMultiValued = functionEvaluationContext.isMultiValued(propertyArgument.getPropertyName()); + + if (isMultiValued) + { + throw new QueryModelException("Like is not supported for multi-valued properties"); + } + + String cmisPropertyName = propertyArgument.getPropertyName(); + + Column column = columnMap.get(cmisPropertyName); + if (column != null) + { + // check for function type + if (column.getFunction().getName().equals(PropertyAccessor.NAME)) + { + PropertyArgument arg = (PropertyArgument) column.getFunctionArguments().get( + PropertyAccessor.ARG_PROPERTY); + cmisPropertyName = arg.getPropertyName(); + } else + { + throw new CmisInvalidArgumentException("Complex column reference not supoprted in LIKE " + + cmisPropertyName); + } + } + + PropertyDefinitionWrapper propDef = cmisDictionaryService.findPropertyByQueryName(cmisPropertyName); + if (propDef.getPropertyDefinition().getPropertyType() != PropertyType.STRING) + { + throw new CmisInvalidArgumentException("LIKE is only supported against String types" + cmisPropertyName); + } + } + } + + /** + * @param queryNode CommonTree + * @param factory QueryModelFactory + * @param selectors Map selectors + * @param columns List + * @return ArrayList + */ + private ArrayList buildOrderings(CommonTree queryNode, QueryModelFactory factory, + Map selectors, List columns) + { + ArrayList orderings = new ArrayList(); + CommonTree orderNode = (CommonTree) queryNode.getFirstChildWithType(CMISParser.ORDER); + if (orderNode != null) + { + for (int i = 0; i < orderNode.getChildCount(); i++) + { + CommonTree current = (CommonTree) orderNode.getChild(i); + + CommonTree columnRefNode = (CommonTree) current.getFirstChildWithType(CMISParser.COLUMN_REF); + if (columnRefNode != null) + { + String columnName = columnRefNode.getChild(0).getText(); + String qualifier = ""; + if (columnRefNode.getChildCount() > 1) + { + qualifier = columnRefNode.getChild(1).getText(); + } + + Order order = Order.ASCENDING; + + if (current.getChild(1).getType() == CMISParser.DESC) + { + order = Order.DESCENDING; + } + + Column orderColumn = null; + + if (qualifier.length() == 0) + { + Column match = null; + for (Column column : columns) + { + if (column.getAlias().equals(columnName)) + { + match = column; + break; + } + if (column.getFunction().getName().equals(PropertyAccessor.NAME)) + { + PropertyArgument arg = (PropertyArgument) column.getFunctionArguments().get( + PropertyAccessor.ARG_PROPERTY); + String propertyName = arg.getPropertyName(); + if (propertyName.equals(columnName)) + { + match = column; + break; + } + } + } + // in strict mode the ordered column must be selected + if ((options.getQueryMode() == CMISQueryMode.CMS_STRICT) && (match == null)) + { + throw new CmisInvalidArgumentException("Ordered column is not selected: " + qualifier + "." + + columnName); + } + if (match == null) + { + + Selector selector = selectors.get(qualifier); + if (selector == null) + { + if ((qualifier.equals("")) && (selectors.size() == 1)) + { + selector = selectors.get(selectors.keySet().iterator().next()); + } else + { + throw new CmisInvalidArgumentException("No selector for " + qualifier); + } + } + + TypeDefinitionWrapper typeDef = cmisDictionaryService.findTypeForClass(selector.getType(), validScopes); + if (typeDef == null) + { + throw new CmisInvalidArgumentException("Type unsupported in CMIS queries: " + + selector.getAlias()); + } + PropertyDefinitionWrapper propDef = cmisDictionaryService + .findPropertyByQueryName(columnName); + if (propDef == null) + { + throw new CmisInvalidArgumentException("Invalid column for " + + typeDef.getTypeDefinition(false).getQueryName() + "." + columnName); + } + + // Check column/property applies to selector/type + + if (typeDef.getPropertyById(propDef.getPropertyId()) == null) + { + throw new CmisInvalidArgumentException("Invalid column for " + + typeDef.getTypeDefinition(false).getQueryName() + "." + columnName); + } + + // check there is a matching selector + + if (options.getQueryMode() == CMISQueryMode.CMS_STRICT) + { + boolean found = false; + for (Column column : columns) + { + if (column.getFunction().getName().equals(PropertyAccessor.NAME)) + { + PropertyArgument pa = (PropertyArgument) column.getFunctionArguments().get( + PropertyAccessor.ARG_PROPERTY); + if (pa.getPropertyName().equals(propDef.getPropertyId())) + { + found = true; + break; + } + } + } + if (!found) + { + throw new CmisInvalidArgumentException("Ordered column is not selected: " + + qualifier + "." + columnName); + } + } + + Function function = factory.getFunction(PropertyAccessor.NAME); + Argument arg = factory.createPropertyArgument(PropertyAccessor.ARG_PROPERTY, propDef + .getPropertyDefinition().isQueryable(), propDef.getPropertyDefinition() + .isOrderable(), selector.getAlias(), propDef.getPropertyId()); + Map functionArguments = new LinkedHashMap(); + functionArguments.put(arg.getName(), arg); + + String alias = (selector.getAlias().length() > 0) ? selector.getAlias() + "." + + propDef.getPropertyId() : propDef.getPropertyId(); + + match = factory.createColumn(function, functionArguments, alias); + } + + orderColumn = match; + } else + { + Selector selector = selectors.get(qualifier); + if (selector == null) + { + if ((qualifier.equals("")) && (selectors.size() == 1)) + { + selector = selectors.get(selectors.keySet().iterator().next()); + } else + { + throw new CmisInvalidArgumentException("No selector for " + qualifier); + } + } + + TypeDefinitionWrapper typeDef = cmisDictionaryService.findTypeForClass(selector.getType(), validScopes); + if (typeDef == null) + { + throw new CmisInvalidArgumentException("Type unsupported in CMIS queries: " + + selector.getAlias()); + } + PropertyDefinitionWrapper propDef = cmisDictionaryService.findPropertyByQueryName(columnName); + if (propDef == null) + { + throw new CmisInvalidArgumentException("Invalid column for " + + typeDef.getTypeDefinition(false).getQueryName() + "." + columnName + + " selector alias " + selector.getAlias()); + } + + // check there is a matching selector + + if (options.getQueryMode() == CMISQueryMode.CMS_STRICT) + { + boolean found = false; + for (Column column : columns) + { + if (column.getFunction().getName().equals(PropertyAccessor.NAME)) + { + PropertyArgument pa = (PropertyArgument) column.getFunctionArguments().get( + PropertyAccessor.ARG_PROPERTY); + if (pa.getPropertyName().equals(propDef.getPropertyId())) + { + found = true; + break; + } + } + } + if (!found) + { + throw new CmisInvalidArgumentException("Ordered column is not selected: " + qualifier + + "." + columnName); + } + } + + Function function = factory.getFunction(PropertyAccessor.NAME); + Argument arg = factory.createPropertyArgument(PropertyAccessor.ARG_PROPERTY, propDef + .getPropertyDefinition().isQueryable(), propDef.getPropertyDefinition().isOrderable(), + selector.getAlias(), propDef.getPropertyId()); + Map functionArguments = new LinkedHashMap(); + functionArguments.put(arg.getName(), arg); + + String alias = (selector.getAlias().length() > 0) ? selector.getAlias() + "." + + propDef.getPropertyId() : propDef.getPropertyId(); + + orderColumn = factory.createColumn(function, functionArguments, alias); + } + + if (!orderColumn.isOrderable() || !orderColumn.isQueryable()) + { + throw new CmisInvalidArgumentException("Ordering is not support for " + orderColumn.getAlias()); + } + + Ordering ordering = factory.createOrdering(orderColumn, order); + orderings.add(ordering); + + } + } + } + return orderings; + } + + @SuppressWarnings("unchecked") + private ArrayList buildColumns(CommonTree queryNode, QueryModelFactory factory, + Map selectors, String query) + { + ArrayList columns = new ArrayList(); + CommonTree starNode = (CommonTree) queryNode.getFirstChildWithType(CMISParser.ALL_COLUMNS); + if (starNode != null) + { + for (Selector selector : selectors.values()) + { + TypeDefinitionWrapper typeDef = cmisDictionaryService.findTypeForClass(selector.getType(), validScopes); + if (typeDef == null) + { + throw new CmisInvalidArgumentException("Type unsupported in CMIS queries: " + selector.getAlias()); + } + Collection propDefs = typeDef.getProperties(); + for (PropertyDefinitionWrapper definition : propDefs) + { + Function function = factory.getFunction(PropertyAccessor.NAME); + Argument arg = factory.createPropertyArgument(PropertyAccessor.ARG_PROPERTY, definition + .getPropertyDefinition().isQueryable(), definition.getPropertyDefinition().isOrderable(), + selector.getAlias(), definition.getPropertyId()); + Map functionArguments = new LinkedHashMap(); + functionArguments.put(arg.getName(), arg); + String alias = (selector.getAlias().length() > 0) ? selector.getAlias() + "." + + definition.getPropertyId() : definition.getPropertyId(); + Column column = factory.createColumn(function, functionArguments, alias); + columns.add(column); + } + } + } + + CommonTree columnsNode = (CommonTree) queryNode.getFirstChildWithType(CMISParser.COLUMNS); + if (columnsNode != null) + { + for (CommonTree columnNode : (List) columnsNode.getChildren()) + { + if (columnNode.getType() == CMISParser.ALL_COLUMNS) + { + String qualifier = columnNode.getChild(0).getText(); + Selector selector = selectors.get(qualifier); + if (selector == null) + { + if ((qualifier.equals("")) && (selectors.size() == 1)) + { + selector = selectors.get(selectors.keySet().iterator().next()); + } else + { + throw new CmisInvalidArgumentException("No selector for " + qualifier + " in " + qualifier + + ".*"); + } + } + + TypeDefinitionWrapper typeDef = cmisDictionaryService.findTypeForClass(selector.getType(), + validScopes); + if (typeDef == null) + { + throw new CmisInvalidArgumentException("Type unsupported in CMIS queries: " + + selector.getAlias()); + } + Collection propDefs = typeDef.getProperties(); + for (PropertyDefinitionWrapper definition : propDefs) + { + Function function = factory.getFunction(PropertyAccessor.NAME); + Argument arg = factory.createPropertyArgument(PropertyAccessor.ARG_PROPERTY, definition + .getPropertyDefinition().isQueryable(), definition.getPropertyDefinition() + .isOrderable(), selector.getAlias(), definition.getPropertyId()); + Map functionArguments = new LinkedHashMap(); + functionArguments.put(arg.getName(), arg); + String alias = (selector.getAlias().length() > 0) ? selector.getAlias() + "." + + definition.getPropertyId() : definition.getPropertyId(); + Column column = factory.createColumn(function, functionArguments, alias); + columns.add(column); + } + } + + if (columnNode.getType() == CMISParser.COLUMN) + { + CommonTree columnRefNode = (CommonTree) columnNode.getFirstChildWithType(CMISParser.COLUMN_REF); + if (columnRefNode != null) + { + String columnName = columnRefNode.getChild(0).getText(); + String qualifier = ""; + if (columnRefNode.getChildCount() > 1) + { + qualifier = columnRefNode.getChild(1).getText(); + } + Selector selector = selectors.get(qualifier); + if (selector == null) + { + if ((qualifier.equals("")) && (selectors.size() == 1)) + { + selector = selectors.get(selectors.keySet().iterator().next()); + } else + { + throw new CmisInvalidArgumentException("No selector for " + qualifier); + } + } + + TypeDefinitionWrapper typeDef = cmisDictionaryService.findTypeForClass(selector.getType(), + validScopes); + if (typeDef == null) + { + throw new CmisInvalidArgumentException("Type unsupported in CMIS queries: " + + selector.getAlias()); + } + PropertyDefinitionWrapper propDef = cmisDictionaryService.findPropertyByQueryName(columnName); + if (propDef == null) + { + throw new CmisInvalidArgumentException("Invalid column for " + + typeDef.getTypeDefinition(false).getQueryName() + " " + columnName); + } + + // Check column/property applies to selector/type + + if (typeDef.getPropertyById(propDef.getPropertyId()) == null) + { + throw new CmisInvalidArgumentException("Invalid column for " + + typeDef.getTypeDefinition(false).getQueryName() + "." + columnName); + } + + Function function = factory.getFunction(PropertyAccessor.NAME); + Argument arg = factory.createPropertyArgument(PropertyAccessor.ARG_PROPERTY, propDef + .getPropertyDefinition().isQueryable(), propDef.getPropertyDefinition().isOrderable(), + selector.getAlias(), propDef.getPropertyId()); + Map functionArguments = new LinkedHashMap(); + functionArguments.put(arg.getName(), arg); + + String alias = (selector.getAlias().length() > 0) ? selector.getAlias() + "." + + propDef.getPropertyId() : propDef.getPropertyId(); + if (columnNode.getChildCount() > 1) + { + alias = columnNode.getChild(1).getText(); + } + + Column column = factory.createColumn(function, functionArguments, alias); + columns.add(column); + + } + + CommonTree functionNode = (CommonTree) columnNode.getFirstChildWithType(CMISParser.FUNCTION); + if (functionNode != null) + { + CommonTree functionNameNode = (CommonTree) functionNode.getChild(0); + Function function = factory.getFunction(functionNameNode.getText()); + if (function == null) + { + throw new CmisInvalidArgumentException("Unknown function: " + functionNameNode.getText()); + } + Collection definitions = function.getArgumentDefinitions().values(); + Map functionArguments = new LinkedHashMap(); + + int childIndex = 2; + for (ArgumentDefinition definition : definitions) + { + if (functionNode.getChildCount() > childIndex + 1) + { + CommonTree argNode = (CommonTree) functionNode.getChild(childIndex++); + Argument arg = getFunctionArgument(argNode, definition, factory, selectors, null, + function.getName().equals(Like.NAME)); + functionArguments.put(arg.getName(), arg); + } else + { + if (definition.isMandatory()) + { + // throw new + // CmisInvalidArgumentException("Insufficient + // aruments + // for function " + + // ((CommonTree) + // functionNode.getChild(0)).getText() ); + break; + } else + { + // ok + } + } + } + + CommonTree rparenNode = (CommonTree) functionNode.getChild(functionNode.getChildCount() - 1); + + int start = getStringPosition(query, functionNode.getLine(), + functionNode.getCharPositionInLine()); + int end = getStringPosition(query, rparenNode.getLine(), rparenNode.getCharPositionInLine()); + + if (function.getName().equals(Score.NAME)) + { + hasScore = true; + } + + String alias; + if (function.getName().equals(Score.NAME)) + { + alias = "SEARCH_SCORE"; + // check no args + if (functionNode.getChildCount() > 3) + { + throw new CmisInvalidArgumentException( + "The function SCORE() is not allowed any arguments"); + } + } else + { + alias = query.substring(start, end + 1); + } + if (columnNode.getChildCount() > 1) + { + alias = columnNode.getChild(1).getText(); + } + + Column column = factory.createColumn(function, functionArguments, alias); + columns.add(column); + } + } + } + } + + return columns; + } + + /** + * @param query String + * @param line int + * @param charPositionInLine int + * @return int + */ + private int getStringPosition(String query, int line, int charPositionInLine) + { + StringTokenizer tokenizer = new StringTokenizer(query, "\n\r\f"); + String[] lines = new String[tokenizer.countTokens()]; + int i = 0; + while (tokenizer.hasMoreElements()) + { + lines[i++] = tokenizer.nextToken(); + } + + int position = 0; + for (i = 0; i < line - 1; i++) + { + position += lines[i].length(); + position++; + } + return position + charPositionInLine; + } + + private Argument getFunctionArgument(CommonTree argNode, ArgumentDefinition definition, QueryModelFactory factory, + Map selectors, Map columnMap, boolean inLike) + { + if (argNode.getType() == CMISParser.COLUMN_REF) + { + PropertyArgument arg = buildColumnReference(definition.getName(), argNode, factory, selectors, columnMap); + if (!arg.isQueryable()) + { + throw new CmisInvalidArgumentException("Column refers to unqueryable property " + arg.getPropertyName()); + } + if (!selectors.containsKey(arg.getSelector())) + { + throw new CmisInvalidArgumentException("No table with alias " + arg.getSelector()); + } + return arg; + } else if (argNode.getType() == CMISParser.ID) + { + String id = argNode.getText(); + if (selectors.containsKey(id)) + { + SelectorArgument arg = factory.createSelectorArgument(definition.getName(), id); + if (!arg.isQueryable()) + { + throw new CmisInvalidArgumentException("Selector is not queryable " + arg.getSelector()); + } + return arg; + } else + { + PropertyDefinitionWrapper propDef = cmisDictionaryService.findPropertyByQueryName(id); + if (propDef == null || !propDef.getPropertyDefinition().isQueryable()) + { + throw new CmisInvalidArgumentException("Column refers to unqueryable property " + + definition.getName()); + } + PropertyArgument arg = factory.createPropertyArgument(definition.getName(), propDef + .getPropertyDefinition().isQueryable(), propDef.getPropertyDefinition().isOrderable(), "", + propDef.getPropertyId()); + return arg; + } + } else if (argNode.getType() == CMISParser.PARAMETER) + { + ParameterArgument arg = factory.createParameterArgument(definition.getName(), argNode.getText()); + if (!arg.isQueryable()) + { + throw new CmisInvalidArgumentException("Parameter is not queryable " + arg.getParameterName()); + } + return arg; + } else if (argNode.getType() == CMISParser.NUMERIC_LITERAL) + { + CommonTree literalNode = (CommonTree) argNode.getChild(0); + if (literalNode.getType() == CMISParser.FLOATING_POINT_LITERAL) + { + QName type = DataTypeDefinition.DOUBLE; + Number value = Double.parseDouble(literalNode.getText()); + if (value.floatValue() == value.doubleValue()) + { + type = DataTypeDefinition.FLOAT; + value = Float.valueOf(value.floatValue()); + } + LiteralArgument arg = factory.createLiteralArgument(definition.getName(), type, value); + return arg; + } else if (literalNode.getType() == CMISParser.DECIMAL_INTEGER_LITERAL) + { + QName type = DataTypeDefinition.LONG; + Number value = Long.parseLong(literalNode.getText()); + if (value.intValue() == value.longValue()) + { + type = DataTypeDefinition.INT; + value = Integer.valueOf(value.intValue()); + } + LiteralArgument arg = factory.createLiteralArgument(definition.getName(), type, value); + return arg; + } else + { + throw new CmisInvalidArgumentException("Invalid numeric literal " + literalNode.getText()); + } + } else if (argNode.getType() == CMISParser.STRING_LITERAL) + { + String text = argNode.getChild(0).getText(); + text = text.substring(1, text.length() - 1); + text = unescape(text, inLike ? EscapeMode.LIKE : EscapeMode.LITERAL); + LiteralArgument arg = factory.createLiteralArgument(definition.getName(), DataTypeDefinition.TEXT, text); + return arg; + } else if (argNode.getType() == CMISParser.DATETIME_LITERAL) + { + String text = argNode.getChild(0).getText(); + text = text.substring(1, text.length() - 1); + StringBuilder builder = new StringBuilder(); + if (text.endsWith("Z")) + { + builder.append(text.substring(0, text.length() - 1)); + builder.append("+0000"); + } else + { + if (text.charAt(text.length() - 3) != ':') + { + throw new CmisInvalidArgumentException("Invalid datetime literal " + text); + } + // remove TZ colon .... + builder.append(text.substring(0, text.length() - 3)); + builder.append(text.substring(text.length() - 2, text.length())); + } + text = builder.toString(); + + SimpleDateFormat df = CachingDateFormat.getCmisSqlDatetimeFormat(); + Date date; + try + { + date = df.parse(text); + } catch (ParseException e) + { + throw new CmisInvalidArgumentException("Invalid datetime literal " + text); + } + // Convert back :-) + String alfrescoDate = DefaultTypeConverter.INSTANCE.convert(String.class, date); + LiteralArgument arg = factory.createLiteralArgument(definition.getName(), DataTypeDefinition.TEXT, + alfrescoDate); + return arg; + } else if (argNode.getType() == CMISParser.BOOLEAN_LITERAL) + { + String text = argNode.getChild(0).getText(); + if (text.equalsIgnoreCase("TRUE") || text.equalsIgnoreCase("FALSE")) + { + LiteralArgument arg = factory + .createLiteralArgument(definition.getName(), DataTypeDefinition.TEXT, text); + return arg; + } else + { + throw new CmisInvalidArgumentException("Invalid boolean literal " + text); + } + + } else if (argNode.getType() == CMISParser.LIST) + { + ArrayList arguments = new ArrayList(); + for (int i = 0; i < argNode.getChildCount(); i++) + { + CommonTree arg = (CommonTree) argNode.getChild(i); + arguments.add(getFunctionArgument(arg, definition, factory, selectors, columnMap, inLike)); + } + ListArgument arg = factory.createListArgument(definition.getName(), arguments); + if (!arg.isQueryable()) + { + throw new CmisInvalidArgumentException("Not all members of the list are queryable"); + } + return arg; + } else if (argNode.getType() == CMISParser.ANY) + { + LiteralArgument arg = factory.createLiteralArgument(definition.getName(), DataTypeDefinition.TEXT, + argNode.getText()); + return arg; + } else if (argNode.getType() == CMISParser.SINGLE_VALUED_PROPERTY) + { + LiteralArgument arg = factory.createLiteralArgument(definition.getName(), DataTypeDefinition.TEXT, + argNode.getText()); + return arg; + } else if (argNode.getType() == CMISParser.NOT) + { + LiteralArgument arg = factory.createLiteralArgument(definition.getName(), DataTypeDefinition.TEXT, + argNode.getText()); + return arg; + } else if (argNode.getType() == CMISParser.FUNCTION) + { + CommonTree functionNameNode = (CommonTree) argNode.getChild(0); + Function function = factory.getFunction(functionNameNode.getText()); + if (function == null) + { + throw new CmisInvalidArgumentException("Unknown function: " + functionNameNode.getText()); + } + Collection definitions = function.getArgumentDefinitions().values(); + Map functionArguments = new LinkedHashMap(); + + int childIndex = 2; + for (ArgumentDefinition currentDefinition : definitions) + { + if (argNode.getChildCount() > childIndex + 1) + { + CommonTree currentArgNode = (CommonTree) argNode.getChild(childIndex++); + Argument arg = getFunctionArgument(currentArgNode, currentDefinition, factory, selectors, + columnMap, inLike); + functionArguments.put(arg.getName(), arg); + } else + { + if (definition.isMandatory()) + { + // throw new CmisInvalidArgumentException("Insufficient + // aruments + // for function " + ((CommonTree) + // functionNode.getChild(0)).getText() ); + break; + } else + { + // ok + } + } + } + FunctionArgument arg = factory.createFunctionArgument(definition.getName(), function, functionArguments); + if (!arg.isQueryable()) + { + throw new CmisInvalidArgumentException("Not all function arguments refer to orderable arguments: " + + arg.getFunction().getName()); + } + return arg; + } else + { + throw new CmisInvalidArgumentException("Invalid function argument " + argNode.getText()); + } + } + + @SuppressWarnings("unchecked") + private Source buildSource(CommonTree source, CapabilityJoin joinSupport, QueryModelFactory factory) + { + if (source.getChildCount() == 1) + { + // single table reference + CommonTree singleTableNode = (CommonTree) source.getChild(0); + if (singleTableNode.getType() == CMISParser.TABLE) + { + if (joinSupport == CapabilityJoin.NONE) + { + throw new UnsupportedOperationException("Joins are not supported"); + } + CommonTree tableSourceNode = (CommonTree) singleTableNode.getFirstChildWithType(CMISParser.SOURCE); + return buildSource(tableSourceNode, joinSupport, factory); + + } + if (singleTableNode.getType() != CMISParser.TABLE_REF) + { + throw new CmisInvalidArgumentException("Expecting TABLE_REF token but found " + + singleTableNode.getText()); + } + String tableName = singleTableNode.getChild(0).getText(); + String alias = ""; + if (singleTableNode.getChildCount() > 1) + { + alias = singleTableNode.getChild(1).getText(); + } + + TypeDefinitionWrapper typeDef = cmisDictionaryService.findTypeByQueryName(tableName); + if (typeDef == null) + { + throw new CmisInvalidArgumentException("Type is unsupported in query: " + tableName); + } + if (typeDef.getBaseTypeId() != BaseTypeId.CMIS_POLICY) + { + if (!typeDef.getTypeDefinition(false).isQueryable()) + { + throw new CmisInvalidArgumentException("Type is not queryable " + tableName + " -> " + + typeDef.getTypeId()); + } + } + return factory.createSelector(typeDef.getAlfrescoClass(), alias); + } else + { + if (joinSupport == CapabilityJoin.NONE) + { + throw new UnsupportedOperationException("Joins are not supported"); + } + CommonTree singleTableNode = (CommonTree) source.getChild(0); + if (singleTableNode.getType() != CMISParser.TABLE_REF) + { + throw new CmisInvalidArgumentException("Expecting TABLE_REF token but found " + + singleTableNode.getText()); + } + String tableName = singleTableNode.getChild(0).getText(); + String alias = ""; + if (singleTableNode.getChildCount() == 2) + { + alias = singleTableNode.getChild(1).getText(); + } + TypeDefinitionWrapper typeDef = cmisDictionaryService.findTypeByQueryName(tableName); + if (typeDef == null) + { + throw new CmisInvalidArgumentException("Type is unsupported in query " + tableName); + } + if (typeDef.getBaseTypeId() != BaseTypeId.CMIS_POLICY) + { + if (!typeDef.getTypeDefinition(false).isQueryable()) + { + throw new CmisInvalidArgumentException("Type is not queryable " + tableName + " -> " + + typeDef.getTypeId()); + } + } + + Source lhs = factory.createSelector(typeDef.getAlfrescoClass(), alias); + + List list = (List) (source.getChildren()); + for (CommonTree joinNode : list) + { + if (joinNode.getType() == CMISParser.JOIN) + { + CommonTree rhsSource = (CommonTree) joinNode.getFirstChildWithType(CMISParser.SOURCE); + Source rhs = buildSource(rhsSource, joinSupport, factory); + + JoinType joinType = JoinType.INNER; + CommonTree joinTypeNode = (CommonTree) joinNode.getFirstChildWithType(CMISParser.LEFT); + if (joinTypeNode != null) + { + joinType = JoinType.LEFT; + } + + if ((joinType == JoinType.LEFT) && (joinSupport == CapabilityJoin.INNERONLY)) + { + throw new UnsupportedOperationException("Outer joins are not supported"); + } + + Constraint joinCondition = null; + CommonTree joinConditionNode = (CommonTree) joinNode.getFirstChildWithType(CMISParser.ON); + if (joinConditionNode != null) + { + PropertyArgument arg1 = buildColumnReference(Equals.ARG_LHS, + (CommonTree) joinConditionNode.getChild(0), factory, null, null); + if (!lhs.getSelectors().containsKey(arg1.getSelector()) + && !rhs.getSelectors().containsKey(arg1.getSelector())) + { + throw new CmisInvalidArgumentException("No table with alias " + arg1.getSelector()); + } + CommonTree functionNameNode = (CommonTree) joinConditionNode.getChild(1); + if (functionNameNode.getType() != CMISParser.EQUALS) + { + throw new CmisInvalidArgumentException("Only Equi-join is supported " + + functionNameNode.getText()); + } + Function function = factory.getFunction(Equals.NAME); + if (function == null) + { + throw new CmisInvalidArgumentException("Unknown function: " + functionNameNode.getText()); + } + PropertyArgument arg2 = buildColumnReference(Equals.ARG_RHS, + (CommonTree) joinConditionNode.getChild(2), factory, null, null); + if (!lhs.getSelectors().containsKey(arg2.getSelector()) + && !rhs.getSelectors().containsKey(arg2.getSelector())) + { + throw new CmisInvalidArgumentException("No table with alias " + arg2.getSelector()); + } + Map functionArguments = new LinkedHashMap(); + functionArguments.put(arg1.getName(), arg1); + functionArguments.put(arg2.getName(), arg2); + joinCondition = factory.createFunctionalConstraint(function, functionArguments); + } + + Source join = factory.createJoin(lhs, rhs, joinType, joinCondition); + lhs = join; + } + } + + return lhs; + + } + } + + public PropertyArgument buildColumnReference(String argumentName, CommonTree columnReferenceNode, + QueryModelFactory factory, Map selectors, Map columnMap) + { + String cmisPropertyName = columnReferenceNode.getChild(0).getText(); + String qualifier = ""; + if (columnReferenceNode.getChildCount() > 1) + { + qualifier = columnReferenceNode.getChild(1).getText(); + } + + if ((qualifier == "") && (columnMap != null)) + { + Column column = columnMap.get(cmisPropertyName); + if (column != null) + { + // check for function type + if (column.getFunction().getName().equals(PropertyAccessor.NAME)) + { + PropertyArgument arg = (PropertyArgument) column.getFunctionArguments().get( + PropertyAccessor.ARG_PROPERTY); + cmisPropertyName = arg.getPropertyName(); + qualifier = arg.getSelector(); + } else + { + // TODO: should be able to return non property arguments + // The implementation should throw out what it can not + // support at build time. + throw new CmisInvalidArgumentException( + "Complex column reference unsupported (only direct column references are currently supported) " + + cmisPropertyName); + } + } + } + + PropertyDefinitionWrapper propDef = cmisDictionaryService.findPropertyByQueryName(cmisPropertyName); + if (propDef == null) + { + throw new CmisInvalidArgumentException("Unknown column/property " + cmisPropertyName); + } + + if (selectors != null) + { + Selector selector = selectors.get(qualifier); + if (selector == null) + { + if ((qualifier.equals("")) && (selectors.size() == 1)) + { + selector = selectors.get(selectors.keySet().iterator().next()); + } else + { + throw new CmisInvalidArgumentException("No selector for " + qualifier); + } + } + + TypeDefinitionWrapper typeDef = cmisDictionaryService.findTypeForClass(selector.getType(), validScopes); + if (typeDef == null) + { + throw new CmisInvalidArgumentException("Type unsupported in CMIS queries: " + selector.getAlias()); + } + + // Check column/property applies to selector/type + + if (typeDef.getPropertyById(propDef.getPropertyId()) == null) + { + throw new CmisInvalidArgumentException("Invalid column for " + + typeDef.getTypeDefinition(false).getQueryName() + "." + cmisPropertyName); + } + } + + if (options.getQueryMode() == CMISQueryMode.CMS_STRICT) + { + if (!propDef.getPropertyDefinition().isQueryable()) + { + throw new CmisInvalidArgumentException("Column is not queryable " + qualifier + "." + cmisPropertyName); + } + } + return factory.createPropertyArgument(argumentName, propDef.getPropertyDefinition().isQueryable(), propDef + .getPropertyDefinition().isOrderable(), qualifier, propDef.getPropertyId()); + } + + private String unescape(String string, EscapeMode mode) + { + StringBuilder builder = new StringBuilder(string.length()); + + boolean lastWasEscape = false; + + for (int i = 0; i < string.length(); i++) + { + char c = string.charAt(i); + if (lastWasEscape) + { + + // Need to keep escaping for like as we have the same escaping + if (mode == EscapeMode.LIKE) + { + // Like does its own escaping - so pass through \ % and _ + if (c == '\'') + { + builder.append(c); + } else if (c == '%') + { + builder.append('\\'); + builder.append(c); + } else if (c == '_') + { + builder.append('\\'); + builder.append(c); + } else if (c == '\\') + { + builder.append('\\'); + builder.append(c); + } else + { + throw new UnsupportedOperationException("Unsupported escape pattern in <" + string + + "> at position " + i); + } + } else if (mode == EscapeMode.CONTAINS) + { + if (options.getQueryMode() == CMISQueryMode.CMS_STRICT) + { + if (c == '\'') + { + builder.append(c); + } else if (c == '\\') + { + builder.append('\\'); + builder.append(c); + } + + else + { + throw new UnsupportedOperationException("Unsupported escape pattern in <" + string + + "> at position " + i); + } + } else + { + if (c == '\'') + { + builder.append(c); + } else + { + builder.append(c); + } + } + } else if (mode == EscapeMode.LITERAL) + { + if (c == '\'') + { + builder.append(c); + } else if (c == '\\') + { + builder.append(c); + } else + { + throw new UnsupportedOperationException("Unsupported escape pattern in <" + string + + "> at position " + i); + + } + } else + { + throw new UnsupportedOperationException("Unsupported escape pattern in <" + string + + "> at position " + i); + + } + lastWasEscape = false; + } else + { + if (c == '\\') + { + lastWasEscape = true; + } else + { + builder.append(c); + } + } + } + if (lastWasEscape) + { + throw new FTSQueryException("Escape character at end of string " + string); + } + + return builder.toString(); + } +} diff --git a/src/main/java/org/alfresco/opencmis/search/CmisFunctionEvaluationContext.java b/src/main/java/org/alfresco/opencmis/search/CmisFunctionEvaluationContext.java new file mode 100644 index 0000000..df6d9ed --- /dev/null +++ b/src/main/java/org/alfresco/opencmis/search/CmisFunctionEvaluationContext.java @@ -0,0 +1,552 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.opencmis.search; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; + +import org.alfresco.opencmis.dictionary.CMISDictionaryService; +import org.alfresco.opencmis.dictionary.CMISNodeInfo; +import org.alfresco.opencmis.dictionary.PropertyDefinitionWrapper; +import org.alfresco.opencmis.dictionary.TypeDefinitionWrapper; +import org.alfresco.repo.search.adaptor.lucene.LuceneFunction; +import org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor; +import org.alfresco.repo.search.adaptor.lucene.QueryConstants; +import org.alfresco.repo.search.impl.querymodel.FunctionArgument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.PredicateMode; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.Selector; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Lower; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Upper; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.apache.chemistry.opencmis.commons.PropertyIds; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.enums.Cardinality; +import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; + +/** + * @author andyh + */ +@SuppressWarnings("deprecation") +public class CmisFunctionEvaluationContext implements FunctionEvaluationContext +{ + private static HashSet EXPOSED_FIELDS = new HashSet(); + + public static BaseTypeId[] STRICT_SCOPES = new BaseTypeId[] { BaseTypeId.CMIS_DOCUMENT, BaseTypeId.CMIS_FOLDER }; + + public static BaseTypeId[] ALFRESCO_SCOPES = new BaseTypeId[] { BaseTypeId.CMIS_DOCUMENT, BaseTypeId.CMIS_FOLDER, + BaseTypeId.CMIS_POLICY, BaseTypeId.CMIS_SECONDARY, BaseTypeId.CMIS_ITEM }; + + private Map nodeRefs; + + private Map scores; + + private Map nodeInfos; + + private NodeService nodeService; + + private CMISDictionaryService cmisDictionaryService; + + private BaseTypeId[] validScopes; + + private Float score; + + static + { + EXPOSED_FIELDS.add(QueryConstants.FIELD_PATH); + EXPOSED_FIELDS.add(QueryConstants.FIELD_TEXT); + EXPOSED_FIELDS.add(QueryConstants.FIELD_ID); + EXPOSED_FIELDS.add(QueryConstants.FIELD_ISROOT); + EXPOSED_FIELDS.add(QueryConstants.FIELD_ISNODE); + EXPOSED_FIELDS.add(QueryConstants.FIELD_TX); + EXPOSED_FIELDS.add(QueryConstants.FIELD_PARENT); + EXPOSED_FIELDS.add(QueryConstants.FIELD_PRIMARYPARENT); + EXPOSED_FIELDS.add(QueryConstants.FIELD_QNAME); + EXPOSED_FIELDS.add(QueryConstants.FIELD_CLASS); + EXPOSED_FIELDS.add(QueryConstants.FIELD_TYPE); + EXPOSED_FIELDS.add(QueryConstants.FIELD_EXACTTYPE); + EXPOSED_FIELDS.add(QueryConstants.FIELD_ASPECT); + EXPOSED_FIELDS.add(QueryConstants.FIELD_EXACTASPECT); + EXPOSED_FIELDS.add(QueryConstants.FIELD_ALL); + EXPOSED_FIELDS.add(QueryConstants.FIELD_ISUNSET); + EXPOSED_FIELDS.add(QueryConstants.FIELD_ISNULL); + EXPOSED_FIELDS.add(QueryConstants.FIELD_ISNOTNULL); + EXPOSED_FIELDS.add(QueryConstants.FIELD_FTSSTATUS); + EXPOSED_FIELDS.add(QueryConstants.FIELD_ASSOCTYPEQNAME); + EXPOSED_FIELDS.add(QueryConstants.FIELD_PRIMARYASSOCTYPEQNAME); + EXPOSED_FIELDS.add(QueryConstants.FIELD_DBID); + EXPOSED_FIELDS.add(QueryConstants.FIELD_TAG); + EXPOSED_FIELDS.add(QueryConstants.FIELD_TENANT); + EXPOSED_FIELDS.add(QueryConstants.FIELD_ANCESTOR); + EXPOSED_FIELDS.add(QueryConstants.FIELD_SITE); + EXPOSED_FIELDS.add(QueryConstants.FIELD_TAG); + EXPOSED_FIELDS.add(QueryConstants.FIELD_PNAME); + EXPOSED_FIELDS.add(QueryConstants.FIELD_NPATH); + EXPOSED_FIELDS.add(QueryConstants.FIELD_DOC_TYPE); + + + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.ANY.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.ASSOC_REF.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.BOOLEAN.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.CATEGORY.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.CHILD_ASSOC_REF.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.CONTENT.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.DATE.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.DATETIME.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.DOUBLE.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.FLOAT.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.INT.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.LOCALE.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.LONG.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.MLTEXT.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.NODE_REF.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.PATH.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.PERIOD.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.QNAME.getLocalName()); + EXPOSED_FIELDS.add("d:"+DataTypeDefinition.TEXT.getLocalName()); + } + + + /** + * @param nodeRefs + * the nodeRefs to set + */ + public void setNodeRefs(Map nodeRefs) + { + this.nodeRefs = nodeRefs; + } + + /** + * @param scores + * the scores to set + */ + public void setScores(Map scores) + { + this.scores = scores; + } + + public void setNodeInfos(Map nodeInfos) { + this.nodeInfos = nodeInfos; + } + + /** + * @param nodeService + * the nodeService to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param cmisDictionaryService + * the cmisDictionaryService to set + */ + public void setCmisDictionaryService(CMISDictionaryService cmisDictionaryService) + { + this.cmisDictionaryService = cmisDictionaryService; + } + + /** + * @param validScopes + * the valid scopes to set + */ + public void setValidScopes(BaseTypeId[] validScopes) + { + this.validScopes = validScopes; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext# + * getNodeRefs() + */ + public Map getNodeRefs() + { + return nodeRefs; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext# + * getNodeService() + */ + public NodeService getNodeService() + { + return nodeService; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext# + * getProperty(org.alfresco.service.cmr.repository.NodeRef, + * org.alfresco.service.namespace.QName) + */ + public Serializable getProperty(NodeRef nodeRef, String propertyName) + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(propertyName); + + CMISNodeInfo nodeInfo = nodeInfos.get(nodeRef); + if (nodeInfo == null) + { + nodeInfo = propertyDef.getPropertyAccessor().createNodeInfo(nodeRef); + nodeInfos.put(nodeRef, nodeInfo); + } + + return propertyDef.getPropertyAccessor().getValue(nodeInfo); + } + + /* + * (non-Javadoc) + * + * @see + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext#getScores + * () + */ + public Map getScores() + { + return scores; + } + + /** + * @return the score + */ + public Float getScore() + { + return score; + } + + /** + * @param score + * the score to set + */ + public void setScore(Float score) + { + this.score = score; + } + + public Q buildLuceneEquality(LuceneQueryParserAdaptor lqpa, String propertyName, Serializable value, + PredicateMode mode, LuceneFunction luceneFunction) throws E + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(propertyName); + return propertyDef.getPropertyLuceneBuilder().buildLuceneEquality(lqpa, value, mode, luceneFunction); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext# + * buildLuceneExists(org.alfresco.repo.search.impl.lucene.LuceneQueryParser, + * org.alfresco.service.namespace.QName, java.lang.Boolean) + */ + public Q buildLuceneExists(LuceneQueryParserAdaptor lqpa, String propertyName, Boolean not) throws E + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(propertyName); + return propertyDef.getPropertyLuceneBuilder().buildLuceneExists(lqpa, not); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext# + * buildLuceneGreaterThan + * (org.alfresco.repo.search.impl.lucene.LuceneQueryParser, + * org.alfresco.service.namespace.QName, java.io.Serializable, + * org.alfresco.repo.search.impl.querymodel.PredicateMode) + */ + public Q buildLuceneGreaterThan(LuceneQueryParserAdaptor lqpa, String propertyName, Serializable value, + PredicateMode mode, LuceneFunction luceneFunction) throws E + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(propertyName); + return propertyDef.getPropertyLuceneBuilder().buildLuceneGreaterThan(lqpa, value, mode, luceneFunction); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext# + * buildLuceneGreaterThanOrEquals + * (org.alfresco.repo.search.impl.lucene.LuceneQueryParser, + * org.alfresco.service.namespace.QName, java.io.Serializable, + * org.alfresco.repo.search.impl.querymodel.PredicateMode) + */ + public Q buildLuceneGreaterThanOrEquals(LuceneQueryParserAdaptor lqpa, String propertyName, Serializable value, + PredicateMode mode, LuceneFunction luceneFunction) throws E + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(propertyName); + return propertyDef.getPropertyLuceneBuilder().buildLuceneGreaterThanOrEquals(lqpa, value, mode, luceneFunction); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext# + * buildLuceneIn(org.alfresco.repo.search.impl.lucene.LuceneQueryParser, + * org.alfresco.service.namespace.QName, java.util.Collection, + * java.lang.Boolean, + * org.alfresco.repo.search.impl.querymodel.PredicateMode) + */ + public Q buildLuceneIn(LuceneQueryParserAdaptor lqpa, String propertyName, Collection values, + Boolean not, PredicateMode mode) throws E + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(propertyName); + return propertyDef.getPropertyLuceneBuilder().buildLuceneIn(lqpa, values, not, mode); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext# + * buildLuceneInequality + * (org.alfresco.repo.search.impl.lucene.LuceneQueryParser, + * org.alfresco.service.namespace.QName, java.io.Serializable, + * org.alfresco.repo.search.impl.querymodel.PredicateMode) + */ + public Q buildLuceneInequality(LuceneQueryParserAdaptor lqpa, String propertyName, Serializable value, + PredicateMode mode, LuceneFunction luceneFunction) throws E + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(propertyName); + return propertyDef.getPropertyLuceneBuilder().buildLuceneInequality(lqpa, value, mode, luceneFunction); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext# + * buildLuceneLessThan + * (org.alfresco.repo.search.impl.lucene.LuceneQueryParser, + * org.alfresco.service.namespace.QName, java.io.Serializable, + * org.alfresco.repo.search.impl.querymodel.PredicateMode) + */ + public Q buildLuceneLessThan(LuceneQueryParserAdaptor lqpa, String propertyName, Serializable value, + PredicateMode mode, LuceneFunction luceneFunction) throws E + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(propertyName); + return propertyDef.getPropertyLuceneBuilder().buildLuceneLessThan(lqpa, value, mode, luceneFunction); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext# + * buildLuceneLessThanOrEquals + * (org.alfresco.repo.search.impl.lucene.LuceneQueryParser, + * org.alfresco.service.namespace.QName, java.io.Serializable, + * org.alfresco.repo.search.impl.querymodel.PredicateMode) + */ + public Q buildLuceneLessThanOrEquals(LuceneQueryParserAdaptor lqpa, String propertyName, Serializable value, + PredicateMode mode, LuceneFunction luceneFunction) throws E + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(propertyName); + return propertyDef.getPropertyLuceneBuilder().buildLuceneLessThanOrEquals(lqpa, value, mode, luceneFunction); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext# + * buildLuceneLike(org.alfresco.repo.search.impl.lucene.LuceneQueryParser, + * org.alfresco.service.namespace.QName, java.io.Serializable, + * java.lang.Boolean) + */ + public Q buildLuceneLike(LuceneQueryParserAdaptor lqpa, String propertyName, Serializable value, Boolean not) + throws E + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(propertyName); + return propertyDef.getPropertyLuceneBuilder().buildLuceneLike(lqpa, value, not); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext# + * getLuceneSortField(org.alfresco.service.namespace.QName) + */ + public String getLuceneSortField(LuceneQueryParserAdaptor lqpa, String propertyName) throws E + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(propertyName); + return propertyDef.getPropertyLuceneBuilder().getLuceneSortField(lqpa); + } + + public boolean isObjectId(String propertyName) + { + return PropertyIds.OBJECT_ID.equalsIgnoreCase(propertyName); + } + + public boolean isOrderable(String fieldName) + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(fieldName); + if (propertyDef == null) + { + return false; + } else + { + return propertyDef.getPropertyDefinition().isOrderable(); + } + } + + public boolean isQueryable(String fieldName) + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(fieldName); + if (propertyDef == null) + { + return true; + } else + { + return propertyDef.getPropertyDefinition().isQueryable(); + } + } + + public String getLuceneFieldName(String propertyName) + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(propertyName); + if (propertyDef != null) + { + return propertyDef.getPropertyLuceneBuilder().getLuceneFieldName(); + } else + { + // TODO: restrict to supported "special" fields + return propertyName; + } + } + + public LuceneFunction getLuceneFunction(FunctionArgument functionArgument) + { + if (functionArgument == null) + { + return LuceneFunction.FIELD; + } else + { + String functionName = functionArgument.getFunction().getName(); + if (functionName.equals(Upper.NAME)) + { + return LuceneFunction.UPPER; + } else if (functionName.equals(Lower.NAME)) + { + return LuceneFunction.LOWER; + } else + { + throw new QueryModelException("Unsupported function: " + functionName); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext# + * checkFieldApplies(org.alfresco.service.namespace.QName, java.lang.String) + */ + public void checkFieldApplies(Selector selector, String propertyName) + { + PropertyDefinitionWrapper propDef = cmisDictionaryService.findPropertyByQueryName(propertyName); + if (propDef == null) + { + if (EXPOSED_FIELDS.contains(propertyName)) + { + return; + } + else + { + throw new CmisInvalidArgumentException("Unknown column/property " + propertyName); + } + } + + TypeDefinitionWrapper typeDef = cmisDictionaryService.findTypeForClass(selector.getType(), validScopes); + if (typeDef == null) + { + throw new CmisInvalidArgumentException("Type unsupported in CMIS queries: " + selector.getAlias()); + } + + // Check column/property applies to selector/type + + if (typeDef.getPropertyById(propDef.getPropertyId()) == null) + { + throw new CmisInvalidArgumentException("Invalid column for " + + typeDef.getTypeDefinition(false).getQueryName() + "." + propertyName); + } + + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext# + * isMultiValued(java.lang.String) + */ + public boolean isMultiValued(String propertyName) + { + PropertyDefinitionWrapper propDef = cmisDictionaryService.findPropertyByQueryName(propertyName); + if (propDef == null) + { + throw new CmisInvalidArgumentException("Unknown column/property " + propertyName); + } + return propDef.getPropertyDefinition().getCardinality() == Cardinality.MULTI; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext#getAlfrescoQName(org.alfresco.service.namespace.QName) + */ + @Override + public String getAlfrescoPropertyName(String propertyName) + { + PropertyDefinitionWrapper propertyDef = cmisDictionaryService.findProperty(propertyName); + if(propertyDef != null) + { + return propertyDef.getPropertyLuceneBuilder().getLuceneFieldName().substring(1); + } + else + { + throw new CmisInvalidArgumentException("Unknown column/property " + propertyName); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext#getAlfrescoTypeName(java.lang.String) + */ + @Override + public String getAlfrescoTypeName(String typeName) + { + TypeDefinitionWrapper typeDef = cmisDictionaryService.findType(typeName); + if(typeDef != null) + { + return typeDef.getAlfrescoClass().toString(); + } + else + { + throw new CmisInvalidArgumentException("Unknown type " + typeName); + } + } + +} diff --git a/src/main/java/org/alfresco/repo/cache/LockingCache.java b/src/main/java/org/alfresco/repo/cache/LockingCache.java new file mode 100644 index 0000000..316d265 --- /dev/null +++ b/src/main/java/org/alfresco/repo/cache/LockingCache.java @@ -0,0 +1,61 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.cache; + +import java.io.Serializable; + +/** + * Interface for caches that support value locking + * + * @author Derek Hulley + * @since 4.1.6 + */ +public interface LockingCache extends SimpleCache +{ + /** + * Determine if a value (addition, removal or update) has been locked for the remainer of the transaction + * + * @param key the cache key to check up on + * @return true if the value will not change for the remaineder of the transaction + */ + boolean isValueLocked(K key); + + /** + * Prevent a key's value from being changed for the duriation of the transaction. + * By default, further attempts to modify the associated value will be ignored; + * this includes add a value back after removal. + * + * @param key the cache key that will be locked against change + */ + void lockValue(K key); + + /** + * Cancel any previous lock applied to a key's value. + * + * @param key the cache key that will be unlocked, allowing changes + */ + void unlockValue(K key); +} diff --git a/src/main/java/org/alfresco/repo/cache/MemoryCache.java b/src/main/java/org/alfresco/repo/cache/MemoryCache.java new file mode 100644 index 0000000..882760b --- /dev/null +++ b/src/main/java/org/alfresco/repo/cache/MemoryCache.java @@ -0,0 +1,79 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.cache; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A cache backed by a simple ConcurrentHashMap. + *

+ * Note: This cache is not transaction-safe. Use it for tests or wrap it appropriately. + * + * @author Derek Hulley + * @since 3.2 + */ +public class MemoryCache implements SimpleCache +{ + private Map map; + + public MemoryCache() + { + map = new ConcurrentHashMap(15); + } + + public boolean contains(K key) + { + return map.containsKey(key); + } + + public Collection getKeys() + { + return map.keySet(); + } + + public V get(K key) + { + return map.get(key); + } + + public void put(K key, V value) + { + map.put(key, value); + } + + public void remove(K key) + { + map.remove(key); + } + + public void clear() + { + map.clear(); + } +} diff --git a/src/main/java/org/alfresco/repo/cache/SimpleCache.java b/src/main/java/org/alfresco/repo/cache/SimpleCache.java new file mode 100644 index 0000000..eb49ea7 --- /dev/null +++ b/src/main/java/org/alfresco/repo/cache/SimpleCache.java @@ -0,0 +1,93 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.cache; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; + +import org.alfresco.api.AlfrescoPublicApi; + +/** + * Basic caching interface. + *

+ * All implementations must be thread-safe. Additionally, the use of the + * Serializable for both keys and values ensures that the underlying + * cache implementations can support both clustered caches as well as persistent + * caches. + *

+ * All implementations must support null values. It therefore follows + * that + *

+ *    (simpleCache.contains(key) == true) does not imply (simpleCache.get(key) != null)
+ * 
+ * but + *
+ *    (simpleCache.contains(key) == false) implies (simpleCache.get(key) == null)
+ * 
+ * + * @author Derek Hulley + */ +@AlfrescoPublicApi +public interface SimpleCache +{ + /** + * @param key the cache key to check up on + * @return Returns true if there is a cache entry, + * regardless of whether the value itself is null + */ + public boolean contains(K key); + + public Collection getKeys(); + + /** + * @param key K + * @return Returns the value associated with the key. It will be null + * if the value is null or if the cache doesn't have an entry. + */ + public V get(K key); + + /** + * Set the value to store for a given key. + *

+ * Be sure to use {@link #remove(Serializable) remove} if cache entries need to be removed + * as the cache implementations must treat a null value as a first class object + * in exactly the same way as a {@link Map} will allow storage and retrieval of null values. + * + * @param key the key against which to store the value + * @param value the value to store. null is allowed. + */ + public void put(K key, V value); + + /** + * Removes the cache entry whether or not the value stored against it is null. + * + * @param key the key value to remove + */ + public void remove(K key); + + public void clear(); +} diff --git a/src/main/java/org/alfresco/repo/content/ContentContext.java b/src/main/java/org/alfresco/repo/content/ContentContext.java new file mode 100644 index 0000000..7ff9bf3 --- /dev/null +++ b/src/main/java/org/alfresco/repo/content/ContentContext.java @@ -0,0 +1,92 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.content; + +import java.io.Serializable; + +import org.alfresco.service.cmr.repository.ContentReader; + +/** + * The location and lookup data for content. The very least data required to + * find content or assign a content writer is the content URL and any previous + * content that may have logically existed. + *

+ * Although this class is doesn't enforce any conditions on the context, + * derived instances may have relationships that need to be maintained between + * various context values. + * + * @author Derek Hulley + */ +public class ContentContext implements Serializable +{ + private static final long serialVersionUID = 6476617391229895125L; + + /** An empty context. */ + public static final ContentContext NULL_CONTEXT = new ContentContext(null, null); + + private ContentReader existingContentReader; + private String contentUrl; + + /** + * Construct the instance with the content URL. + * + * @param existingContentReader content with which to seed the new writer - may be null + * @param contentUrl the content URL - may be null + */ + public ContentContext(ContentReader existingContentReader, String contentUrl) + { + this.existingContentReader = existingContentReader; + this.contentUrl = contentUrl; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(128); + sb.append("ContentContext") + .append("[ contentUrl=").append(getContentUrl()) + .append(", existing=").append((getExistingContentReader() == null ? false : true)) + .append("]"); + return sb.toString(); + } + + /** + * @return Returns the content to seed the writer with - may be null + */ + public ContentReader getExistingContentReader() + { + return existingContentReader; + } + + /** + * @return Returns the content URL for the content's context - may be null + */ + public String getContentUrl() + { + return contentUrl; + } + +} diff --git a/src/main/java/org/alfresco/repo/content/ContentStore.java b/src/main/java/org/alfresco/repo/content/ContentStore.java new file mode 100644 index 0000000..8109e72 --- /dev/null +++ b/src/main/java/org/alfresco/repo/content/ContentStore.java @@ -0,0 +1,284 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.content; + +import java.util.Date; + +import org.alfresco.api.AlfrescoPublicApi; +import org.alfresco.service.cmr.repository.ContentAccessor; +import org.alfresco.service.cmr.repository.ContentIOException; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentStreamListener; +import org.alfresco.service.cmr.repository.ContentWriter; + +/** + * Provides low-level retrieval of content + * {@link org.alfresco.service.cmr.repository.ContentReader readers} and + * {@link org.alfresco.service.cmr.repository.ContentWriter writers}. + *

+ * Implementations of this interface should be soley responsible for + * providing persistence and retrieval of the content against a + * content URL. + *

+ * Content URLs must consist of a prefix or protocol followed by an + * implementation-specific identifier. For example, the content URL format + * for file stores is store://year/month/day/hour/minute/GUID.bin
+ *

    + *
  • store://: prefix identifying an Alfresco content stores + * regardless of the persistence mechanism.
  • + *
  • year: year
  • + *
  • month: 1-based month of the year
  • + *
  • day: 1-based day of the month
  • + *
  • hour: 0-based hour of the day
  • + *
  • minute: 0-based minute of the hour
  • + *
  • GUID: A unique identifier
  • + *
+ *

+ * Where the store cannot handle a particular content URL request, the + * {@code UnsupportedContentUrlException} must be generated. This will allow + * various implementations to provide fallback code to other stores where + * possible. + *

+ * Where a store cannot serve a particular request because the functionality + * is just not available, the UnsupportedOperationException should + * be thrown. Once again, there may be fallback handling provided for these + * situations. + * + * @since 1.0 + * @author Derek Hulley + */ +@AlfrescoPublicApi +public interface ContentStore +{ + /** + * An empty content context used to retrieve completely new content. + * + * @see ContentStore#getWriter(ContentContext) + */ + public static final ContentContext NEW_CONTENT_CONTEXT = new ContentContext(null, null); + /** + * The delimiter that must be found in all URLS, i.e :// + */ + public static final String PROTOCOL_DELIMITER = "://"; + + /** + * Check if the content URL format is supported by the store. + * + * @param contentUrl the content URL to check + * @return Returns true if none of the other methods on the store + * will throw an {@code UnsupportedContentUrlException} when given + * this URL. + * + * @since 2.1 + */ + public boolean isContentUrlSupported(String contentUrl); + + /** + * Check if the store supports write requests. + * + * @return Return true is the store supports write operations + * + * @since 2.1 + */ + public boolean isWriteSupported(); + + /** + * Calculates the remaning free space in the underlying store. + *

+ * NOTE: For efficiency, some implementations may provide a guess. + *

+ * Implementations should focus on calculating a size value quickly, rather than accurately. + * + * @return + * Returns the total, possibly approximate, free space (in bytes) available to the store + * or -1 if no size data is available. + * + * @since 3.3.3 + */ + public long getSpaceFree(); + + /** + * Calculates the total storage space of the underlying store. + *

+ * NOTE: For efficiency, some implementations may provide a guess. + *

+ * Implementations should focus on calculating a size value quickly, rather than accurately. + * + * @return + * Returns the total, possibly approximate, size (in bytes) of the underlying store + * or -1 if no size data is available. + * + * @since 3.3.3 + */ + public long getSpaceTotal(); + + /** + * Get the location where the store is rooted. The format of the returned value will depend on the + * specific implementation of the store. + * + * @return Returns the store's root location or . if no information is available + */ + public String getRootLocation(); + + /** + * Check for the existence of content in the store. + *

+ * The implementation of this may be more efficient than first getting a + * reader to {@link ContentReader#exists() check for existence}, although + * that check should also be performed. + * + * @param contentUrl + * the path to the content + * @return + * Returns true if the content exists, otherwise false if the content doesn't + * exist or if the URL is not applicable to this store. + * @throws org.alfresco.repo.content.UnsupportedContentUrlException + * if the content URL supplied is not supported by the store + * @throws ContentIOException + * if an IO error occurs + * + * @see ContentReader#exists() + */ + public boolean exists(String contentUrl); + + /** + * Get the accessor with which to read from the content at the given URL. + * The reader is stateful and can only be used once. + * + * @param contentUrl the path to where the content is located + * @return Returns a read-only content accessor for the given URL. There may + * be no content at the given URL, but the reader must still be returned. + * @throws org.alfresco.repo.content.UnsupportedContentUrlException + * if the content URL supplied is not supported by the store + * @throws ContentIOException + * if an IO error occurs + * + * @see #exists(String) + * @see ContentReader#exists() + * @see org.alfresco.repo.content.EmptyContentReader + */ + public ContentReader getReader(String contentUrl); + + /** + * Get an accessor with which to write content to a location + * within the store. The writer is stateful and can + * only be used once. The location may be specified but must, in that case, + * be a valid and unused URL. + *

+ * The store will ensure that the {@link ContentAccessor#getContentUrl() new content URL} will + * be valid for all subsequent read attempts. + *

+ * By supplying a reader to existing content, the store implementation may + * enable random access. The store implementation + * can enable this by copying the existing content into the new location + * before supplying a writer onto the new content. + * + * @param context + * the context of content. + * @return + * Returns a write-only content accessor + * @throws UnsupportedOperationException + * if the store is unable to provide the information + * @throws UnsupportedContentUrlException + * if the content URL supplied is not supported by the store + * @throws ContentExistsException + * if the content URL is already in use + * @throws ContentIOException + * if an IO error occurs + * + * @see ContentWriter#addListener(ContentStreamListener) + * @see ContentWriter#getContentUrl() + */ + public ContentWriter getWriter(ContentContext context); + + /** + * Get all URLs for the store, regardless of creation time. + * @throws ContentIOException + * if an IO error occurs + * @throws UnsupportedOperationException + * if the store is unable to provide the information + * + * @see #getUrls(Date, Date, ContentUrlHandler) + * + * @deprecated in 5.0. The API is no longer used by Alfresco; + * efficient APIs can be provided by the implementations, if required + */ + @Deprecated + public void getUrls(ContentUrlHandler handler) throws ContentIOException; + + /** + * Get a set of all content URLs in the store. This indicates all content available for reads. + * + * @param createdAfter + * all URLs returned must have been created after this date. May be null. + * @param createdBefore + * all URLs returned must have been created before this date. May be null. + * @param handler + * the callback that will passed each URL + * @throws ContentIOException + * if an error occurs + * @throws UnsupportedOperationException + * if the store is unable to provide the information + * + * @deprecated in 5.0. The API is no longer used by Alfresco; + * efficient APIs can be provided by the implementations, if required + */ + @Deprecated + public void getUrls(Date createdAfter, Date createdBefore, ContentUrlHandler handler) throws ContentIOException; + + /** + * Deletes the content at the given URL. + *

+ * A delete cannot be forced since it is much better to have the + * file remain longer than desired rather than deleted prematurely. + * + * @param contentUrl + * the URL of the content to delete + * @return + * Returns true if the content was deleted (either by this or another operation), + * otherwise false. If the content no longer exists, then true is returned. + * @throws UnsupportedOperationException + * if the store is unable to perform the action + * @throws UnsupportedContentUrlException + * if the content URL supplied is not supported by the store + * @throws ContentIOException if an error occurs + * if an IO error occurs + */ + public boolean delete(String contentUrl); + + /** + * Iterface for to use during iteration over content URLs. + * + * @author Derek Hulley + * @since 2.0 + * @deprecated in 5.0 + */ + @Deprecated + public interface ContentUrlHandler + { + void handle(String contentUrl); + } +} diff --git a/src/main/java/org/alfresco/repo/content/ContentStoreCaps.java b/src/main/java/org/alfresco/repo/content/ContentStoreCaps.java new file mode 100644 index 0000000..44bdd26 --- /dev/null +++ b/src/main/java/org/alfresco/repo/content/ContentStoreCaps.java @@ -0,0 +1,59 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.content; + +import org.alfresco.repo.tenant.TenantDeployer; + +/** + * ContentStore capabilities. Allows us to avoid performing {@code instanceof} questions + * which can become a problem when certain proxies or subsystems are in use. + *

+ * See ACE-2682 (tenant creation failure) for motivation. + * + * @author Matt Ward + */ +public interface ContentStoreCaps +{ + /** + * Returns the ContentStore cast to a TenantRoutingContentStore if the underlying + * instance is of that type. Returns null otherwise. + *

+ * Note, the actual return type is a TenantDeployer (supertype of TenantRoutingContentStore) + * since the data model has no knowledge of that subtype. This interface may + * need to move to a different project. + * + * @return TenantRoutingContentStore + */ + TenantDeployer getTenantRoutingContentStore(); + + /** + * Returns the ContentStore cast to a TenantDeployer if the underlying + * instance is of that type. Returns null otherwise. + * + * @return TenantDeployer + */ + TenantDeployer getTenantDeployer(); +} diff --git a/src/main/java/org/alfresco/repo/content/MimetypeMap.java b/src/main/java/org/alfresco/repo/content/MimetypeMap.java new file mode 100644 index 0000000..3d63859 --- /dev/null +++ b/src/main/java/org/alfresco/repo/content/MimetypeMap.java @@ -0,0 +1,935 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.content; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeMap; + +import org.alfresco.repo.content.encoding.ContentCharsetFinder; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.FileContentReader; +import org.alfresco.service.cmr.repository.MimetypeService; +import org.alfresco.util.PropertyCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.tika.config.TikaConfig; +import org.apache.tika.detect.DefaultDetector; +import org.apache.tika.detect.Detector; +import org.apache.tika.io.TikaInputStream; +import org.apache.tika.metadata.Metadata; +import org.apache.tika.mime.MediaType; +import org.springframework.extensions.config.Config; +import org.springframework.extensions.config.ConfigElement; +import org.springframework.extensions.config.ConfigLookupContext; +import org.springframework.extensions.config.ConfigService; + +/** + * Provides a bidirectional mapping between well-known mimetypes and the + * registered file extensions. All mimetypes and extensions are stored and + * handled as lowercase. + * + * @author Derek Hulley + */ +public class MimetypeMap implements MimetypeService +{ + + public static final String PREFIX_APPLICATION = "application/"; + + public static final String PREFIX_AUDIO = "audio/"; + + public static final String PREFIX_IMAGE = "image/"; + + public static final String PREFIX_MESSAGE = "message/"; + + public static final String PREFIX_MODEL = "model/"; + + public static final String PREFIX_MULTIPART = "multipart/"; + + public static final String PREFIX_TEXT = "text/"; + + public static final String PREFIX_VIDEO = "video/"; + + public static final String EXTENSION_BINARY = "bin"; + + public static final String MACOS_RESOURCE_FORK_FILE_NAME_PREFIX = "._"; + + public static final String MIMETYPE_MULTIPART_ALTERNATIVE = "multipart/alternative"; + + public static final String MIMETYPE_TEXT_PLAIN = "text/plain"; + + public static final String MIMETYPE_TEXT_MEDIAWIKI = "text/mediawiki"; + + public static final String MIMETYPE_TEXT_CSS = "text/css"; + + public static final String MIMETYPE_TEXT_CSV = "text/csv"; + + public static final String MIMETYPE_TEXT_JAVASCRIPT = "text/javascript"; + + public static final String MIMETYPE_XML = "text/xml"; + + public static final String MIMETYPE_HTML = "text/html"; + + public static final String MIMETYPE_XHTML = "application/xhtml+xml"; + + public static final String MIMETYPE_PDF = "application/pdf"; + + public static final String MIMETYPE_JSON = "application/json"; + + public static final String MIMETYPE_WORD = "application/msword"; + + public static final String MIMETYPE_EXCEL = "application/vnd.ms-excel"; + + public static final String MIMETYPE_BINARY = "application/octet-stream"; + + public static final String MIMETYPE_PPT = "application/vnd.ms-powerpoint"; + + public static final String MIMETYPE_APP_DWG = "application/dwg"; + + public static final String MIMETYPE_IMG_DWG = "image/vnd.dwg"; + + public static final String MIMETYPE_VIDEO_AVI = "video/x-msvideo"; + + public static final String MIMETYPE_VIDEO_QUICKTIME = "video/quicktime"; + + public static final String MIMETYPE_VIDEO_WMV = "video/x-ms-wmv"; + + public static final String MIMETYPE_VIDEO_3GP = "video/3gpp"; + + public static final String MIMETYPE_VIDEO_3GP2 = "video/3gpp2"; + + public static final String MIMETYPE_DITA = "application/dita+xml"; + + // Flash + public static final String MIMETYPE_FLASH = "application/x-shockwave-flash"; + + public static final String MIMETYPE_VIDEO_FLV = "video/x-flv"; + + public static final String MIMETYPE_APPLICATION_FLA = "application/x-fla"; + + public static final String MIMETYPE_VIDEO_MPG = "video/mpeg"; + + public static final String MIMETYPE_VIDEO_MP4 = "video/mp4"; + + public static final String MIMETYPE_IMAGE_GIF = "image/gif"; + + public static final String MIMETYPE_IMAGE_JPEG = "image/jpeg"; + + public static final String MIMETYPE_IMAGE_RGB = "image/x-rgb"; + + public static final String MIMETYPE_IMAGE_SVG = "image/svg+xml"; + + public static final String MIMETYPE_IMAGE_PNG = "image/png"; + + public static final String MIMETYPE_IMAGE_TIFF = "image/tiff"; + + public static final String MIMETYPE_IMAGE_RAW_DNG = "image/x-raw-adobe"; + + public static final String MIMETYPE_IMAGE_RAW_3FR = "image/x-raw-hasselblad"; + + public static final String MIMETYPE_IMAGE_RAW_RAF = "image/x-raw-fuji"; + + public static final String MIMETYPE_IMAGE_RAW_CR2 = "image/x-raw-canon"; + + public static final String MIMETYPE_IMAGE_RAW_K25 = "image/x-raw-kodak"; + + public static final String MIMETYPE_IMAGE_RAW_MRW = "image/x-raw-minolta"; + + public static final String MIMETYPE_IMAGE_RAW_NEF = "image/x-raw-nikon"; + + public static final String MIMETYPE_IMAGE_RAW_ORF = "image/x-raw-olympus"; + + public static final String MIMETYPE_IMAGE_RAW_PEF = "image/x-raw-pentax"; + + public static final String MIMETYPE_IMAGE_RAW_ARW = "image/x-raw-sony"; + + public static final String MIMETYPE_IMAGE_RAW_X3F = "image/x-raw-sigma"; + + public static final String MIMETYPE_IMAGE_RAW_RW2 = "image/x-raw-panasonic"; + + public static final String MIMETYPE_IMAGE_RAW_RWL = "image/x-raw-leica"; + + public static final String MIMETYPE_IMAGE_RAW_R3D = "image/x-raw-red"; + + public static final String MIMETYPE_IMAGE_DWT = "image/x-dwt"; + + public static final String MIMETYPE_APPLICATION_EPS = "application/eps"; + + public static final String MIMETYPE_APPLICATION_PS = "application/postscript"; + + public static final String MIMETYPE_JAVASCRIPT = "application/x-javascript"; + + public static final String MIMETYPE_ZIP = "application/zip"; + + public static final String MIMETYPE_OPENSEARCH_DESCRIPTION = "application/opensearchdescription+xml"; + + public static final String MIMETYPE_ATOM = "application/atom+xml"; + + public static final String MIMETYPE_RSS = "application/rss+xml"; + + public static final String MIMETYPE_RFC822 = "message/rfc822"; + + public static final String MIMETYPE_OUTLOOK_MSG = "application/vnd.ms-outlook"; + + public static final String MIMETYPE_VISIO = "application/vnd.visio"; + + public static final String MIMETYPE_VISIO_2013 = "application/vnd.visio2013"; + + // Adobe + public static final String MIMETYPE_APPLICATION_ILLUSTRATOR = "application/illustrator"; + + public static final String MIMETYPE_APPLICATION_PHOTOSHOP = "image/vnd.adobe.photoshop"; + + //Encrypted office document + public static final String MIMETYPE_ENCRYPTED_OFFICE = "application/x-tika-ooxml-protected"; + + // Open Document + public static final String MIMETYPE_OPENDOCUMENT_TEXT = "application/vnd.oasis.opendocument.text"; + + public static final String MIMETYPE_OPENDOCUMENT_TEXT_TEMPLATE = "application/vnd.oasis.opendocument.text-template"; + + public static final String MIMETYPE_OPENDOCUMENT_GRAPHICS = "application/vnd.oasis.opendocument.graphics"; + + public static final String MIMETYPE_OPENDOCUMENT_GRAPHICS_TEMPLATE = "application/vnd.oasis.opendocument.graphics-template"; + + public static final String MIMETYPE_OPENDOCUMENT_PRESENTATION = "application/vnd.oasis.opendocument.presentation"; + + public static final String MIMETYPE_OPENDOCUMENT_PRESENTATION_TEMPLATE = "application/vnd.oasis.opendocument.presentation-template"; + + public static final String MIMETYPE_OPENDOCUMENT_SPREADSHEET = "application/vnd.oasis.opendocument.spreadsheet"; + + public static final String MIMETYPE_OPENDOCUMENT_SPREADSHEET_TEMPLATE = "application/vnd.oasis.opendocument.spreadsheet-template"; + + public static final String MIMETYPE_OPENDOCUMENT_CHART = "application/vnd.oasis.opendocument.chart"; + + public static final String MIMETYPE_OPENDOCUMENT_CHART_TEMPLATE = "applicationvnd.oasis.opendocument.chart-template"; + + public static final String MIMETYPE_OPENDOCUMENT_IMAGE = "application/vnd.oasis.opendocument.image"; + + public static final String MIMETYPE_OPENDOCUMENT_IMAGE_TEMPLATE = "applicationvnd.oasis.opendocument.image-template"; + + public static final String MIMETYPE_OPENDOCUMENT_FORMULA = "application/vnd.oasis.opendocument.formula"; + + public static final String MIMETYPE_OPENDOCUMENT_FORMULA_TEMPLATE = "applicationvnd.oasis.opendocument.formula-template"; + + public static final String MIMETYPE_OPENDOCUMENT_TEXT_MASTER = "application/vnd.oasis.opendocument.text-master"; + + public static final String MIMETYPE_OPENDOCUMENT_TEXT_WEB = "application/vnd.oasis.opendocument.text-web"; + + public static final String MIMETYPE_OPENDOCUMENT_DATABASE = "application/vnd.oasis.opendocument.database"; + + // Open Office + public static final String MIMETYPE_OPENOFFICE1_WRITER = "application/vnd.sun.xml.writer"; + + public static final String MIMETYPE_OPENOFFICE1_CALC = "application/vnd.sun.xml.calc"; + + public static final String MIMETYPE_OPENOFFICE1_DRAW = "application/vnd.sun.xml.draw"; + + public static final String MIMETYPE_OPENOFFICE1_IMPRESS = "application/vnd.sun.xml.impress"; + + // Open XML + public static final String MIMETYPE_OPENXML_WORDPROCESSING = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; + public static final String MIMETYPE_OPENXML_WORDPROCESSING_MACRO = "application/vnd.ms-word.document.macroenabled.12"; + public static final String MIMETYPE_OPENXML_WORD_TEMPLATE = "application/vnd.openxmlformats-officedocument.wordprocessingml.template"; + public static final String MIMETYPE_OPENXML_WORD_TEMPLATE_MACRO = "application/vnd.ms-word.template.macroenabled.12"; + public static final String MIMETYPE_OPENXML_SPREADSHEET = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + public static final String MIMETYPE_OPENXML_SPREADSHEET_TEMPLATE = "application/vnd.openxmlformats-officedocument.spreadsheetml.template"; + public static final String MIMETYPE_OPENXML_SPREADSHEET_MACRO = "application/vnd.ms-excel.sheet.macroenabled.12"; + public static final String MIMETYPE_OPENXML_SPREADSHEET_TEMPLATE_MACRO = "application/vnd.ms-excel.template.macroenabled.12"; + public static final String MIMETYPE_OPENXML_SPREADSHEET_ADDIN_MACRO = "application/vnd.ms-excel.addin.macroenabled.12"; + public static final String MIMETYPE_OPENXML_SPREADSHEET_BINARY_MACRO = "application/vnd.ms-excel.sheet.binary.macroenabled.12"; + public static final String MIMETYPE_OPENXML_PRESENTATION = "application/vnd.openxmlformats-officedocument.presentationml.presentation"; + public static final String MIMETYPE_OPENXML_PRESENTATION_MACRO = "application/vnd.ms-powerpoint.presentation.macroenabled.12"; + public static final String MIMETYPE_OPENXML_PRESENTATION_SLIDESHOW = "application/vnd.openxmlformats-officedocument.presentationml.slideshow"; + public static final String MIMETYPE_OPENXML_PRESENTATION_SLIDESHOW_MACRO = "application/vnd.ms-powerpoint.slideshow.macroenabled.12"; + public static final String MIMETYPE_OPENXML_PRESENTATION_TEMPLATE = "application/vnd.openxmlformats-officedocument.presentationml.template"; + public static final String MIMETYPE_OPENXML_PRESENTATION_TEMPLATE_MACRO = "application/vnd.ms-powerpoint.template.macroenabled.12"; + public static final String MIMETYPE_OPENXML_PRESENTATION_ADDIN = "application/vnd.ms-powerpoint.addin.macroenabled.12"; + public static final String MIMETYPE_OPENXML_PRESENTATION_SLIDE = "application/vnd.openxmlformats-officedocument.presentationml.slide"; + public static final String MIMETYPE_OPENXML_PRESENTATION_SLIDE_MACRO = "application/vnd.ms-powerpoint.slide.macroenabled.12"; + // Star Office + public static final String MIMETYPE_STAROFFICE5_DRAW = "application/vnd.stardivision.draw"; + + public static final String MIMETYPE_STAROFFICE5_CALC = "application/vnd.stardivision.calc"; + + public static final String MIMETYPE_STAROFFICE5_IMPRESS = "application/vnd.stardivision.impress"; + + public static final String MIMETYPE_STAROFFICE5_IMPRESS_PACKED = "application/vnd.stardivision.impress-packed"; + + public static final String MIMETYPE_STAROFFICE5_CHART = "application/vnd.stardivision.chart"; + + public static final String MIMETYPE_STAROFFICE5_WRITER = "application/vnd.stardivision.writer"; + + public static final String MIMETYPE_STAROFFICE5_WRITER_GLOBAL = "application/vnd.stardivision.writer-global"; + + public static final String MIMETYPE_STAROFFICE5_MATH = "application/vnd.stardivision.math"; + + // Apple iWorks + public static final String MIMETYPE_IWORK_KEYNOTE = "application/vnd.apple.keynote"; + + public static final String MIMETYPE_IWORK_NUMBERS = "application/vnd.apple.numbers"; + + public static final String MIMETYPE_IWORK_PAGES = "application/vnd.apple.pages"; + + //MACOS + public static final String MIMETYPE_APPLEFILE = "application/applefile"; + + // WordPerfect + public static final String MIMETYPE_WORDPERFECT = "application/wordperfect"; + + // Audio + public static final String MIMETYPE_MP3 = "audio/mpeg"; + + public static final String MIMETYPE_AUDIO_MP4 = "audio/mp4"; + + public static final String MIMETYPE_VORBIS = "audio/vorbis"; + + public static final String MIMETYPE_FLAC = "audio/x-flac"; + + // Alfresco + public static final String MIMETYPE_ACP = "application/acp"; + + private static final String CONFIG_AREA = "mimetype-map"; + + private static final String CONFIG_CONDITION = "Mimetype Map"; + + private static final String ELEMENT_MIMETYPES = "mimetypes"; + + private static final String ATTR_MIMETYPE = "mimetype"; + + private static final String ATTR_DISPLAY = "display"; + + private static final String ATTR_DEFAULT = "default"; + + private static final String ATTR_TEXT = "text"; + + private static final Log logger = LogFactory.getLog(MimetypeMap.class); + + private ConfigService configService; + + private ContentCharsetFinder contentCharsetFinder; + + private TikaConfig tikaConfig; + + private Detector detector; + + private List mimetypes; + + private Map extensionsByMimetype; + + private Map mimetypesByExtension; + + private Map displaysByMimetype; + + private Map displaysByExtension; + + private Set textMimetypes; + + /** + * Default constructor + * + * @since 2.1 + */ + public MimetypeMap() + { + } + + @Deprecated + public MimetypeMap(ConfigService configService) + { + logger.warn("MimetypeMap(ConfigService configService) has been deprecated. " + + "Use the default constructor and property 'configService'"); + this.configService = configService; + } + + public ConfigService getConfigService() + { + return configService; + } + + /** + * @param configService the config service to use to read mimetypes from + */ + public void setConfigService(ConfigService configService) + { + this.configService = configService; + } + + /** + * {@inheritDoc} + */ + public ContentCharsetFinder getContentCharsetFinder() + { + return contentCharsetFinder; + } + + /** + * Set the system default content characterset decoder + */ + public void setContentCharsetFinder(ContentCharsetFinder contentCharsetFinder) + { + this.contentCharsetFinder = contentCharsetFinder; + } + + /** + * Injects the TikaConfig to use + * + * @param tikaConfig The Tika Config to use + */ + public void setTikaConfig(TikaConfig tikaConfig) + { + this.tikaConfig = tikaConfig; + } + + /** + * Initialises the map using the configuration service provided + */ + public void init() + { + PropertyCheck.mandatory(this, "configService", configService); + PropertyCheck.mandatory(this, "contentCharsetFinder", contentCharsetFinder); + + // TikaConfig should be given, but work around it if not + if (tikaConfig == null) + { + logger.warn("TikaConfig spring parameter not supplied, using default config"); + setTikaConfig(TikaConfig.getDefaultConfig()); + } + // Create our Tika mimetype detector up-front + // We can then be sure we only have the one, so it's quick (ALF-10813) + detector = new DefaultDetector(tikaConfig.getMimeRepository()); + + // Work out the mappings + this.mimetypes = new ArrayList(40); + this.extensionsByMimetype = new HashMap(59); + this.mimetypesByExtension = new HashMap(59); + this.displaysByMimetype = new TreeMap(); + this.displaysByExtension = new HashMap(59); + this.textMimetypes = new HashSet(23); + + Config config = configService.getConfig(CONFIG_CONDITION, new ConfigLookupContext(CONFIG_AREA)); + ConfigElement mimetypesElement = config.getConfigElement(ELEMENT_MIMETYPES); + List mimetypes = mimetypesElement.getChildren(); + int count = 0; + for (ConfigElement mimetypeElement : mimetypes) + { + count++; + // add to list of mimetypes + String mimetype = mimetypeElement.getAttribute(ATTR_MIMETYPE); + if (mimetype == null || mimetype.length() == 0) + { + logger.warn("Ignoring empty mimetype " + count); + continue; + } + // we store it as lowercase + mimetype = mimetype.toLowerCase(); + boolean replacement = this.mimetypes.contains(mimetype); + if (!replacement) + { + this.mimetypes.add(mimetype); + } + // add to map of mimetype displays + String mimetypeDisplay = mimetypeElement.getAttribute(ATTR_DISPLAY); + if (mimetypeDisplay != null && mimetypeDisplay.length() > 0) + { + String prev = this.displaysByMimetype.put(mimetype, mimetypeDisplay); + if (replacement && prev != null && !mimetypeDisplay.equals(prev)) + { + logger.warn("Replacing " + mimetype + " " + ATTR_DISPLAY + " value '" + prev + "' with '" + + mimetypeDisplay + "'"); + } + } + + // Check if it is a text format + String isTextStr = mimetypeElement.getAttribute(ATTR_TEXT); + boolean isText = Boolean.parseBoolean(isTextStr) || mimetype.startsWith(PREFIX_TEXT); + boolean prevIsText = replacement ? this.textMimetypes.contains(mimetype) : !isText; + if (isText != prevIsText) + { + if (isText) + { + this.textMimetypes.add(mimetype); + } + else if (replacement) + { + this.textMimetypes.remove(mimetype); + } + if (replacement) + { + logger.warn("Replacing " + mimetype + " " + ATTR_TEXT + " value " + + (prevIsText ? "'true' with 'false'" : "'false' with 'true'")); + } + } + + // get all the extensions + List extensions = mimetypeElement.getChildren(); + for (ConfigElement extensionElement : extensions) + { + // add to map of mimetypes by extension + String extension = extensionElement.getValue(); + if (extension == null || extension.length() == 0) + { + logger.warn("Ignoring empty extension for mimetype: " + mimetype); + continue; + } + // put to lowercase + extension = extension.toLowerCase(); + this.mimetypesByExtension.put(extension, mimetype); + // add to map of extension displays + String extensionDisplay = extensionElement.getAttribute(ATTR_DISPLAY); + String prev = this.displaysByExtension.get(extension); + // if no display defined for the extension - use the mimetype's + // display + if ((prev == null) && (extensionDisplay == null || extensionDisplay.length() == 0) + && (mimetypeDisplay != null && mimetypeDisplay.length() > 0)) + { + extensionDisplay = mimetypeDisplay; + } + if (extensionDisplay != null) + { + this.displaysByExtension.put(extension, extensionDisplay); + if (prev != null && !extensionDisplay.equals(prev)) + { + logger.warn("Replacing " + mimetype + " extension " + ATTR_DISPLAY + " value '" + prev + + "' with '" + extensionDisplay + "'"); + } + } + + // add to map of extensions by mimetype if it is the default or + // first extension (prevExtension == null) + String prevExtension = this.extensionsByMimetype.get(mimetype); + String isDefaultStr = extensionElement.getAttribute(ATTR_DEFAULT); + boolean isDefault = Boolean.parseBoolean(isDefaultStr) || prevExtension == null; + if (isDefault) + { + this.extensionsByMimetype.put(mimetype, extension); + if (prevExtension != null && !extension.equals(prevExtension)) + { + logger.warn("Replacing " + mimetype + " default extension '" + prevExtension + "' with '" + + extension + "'"); + } + } + } + // check that there were extensions defined + if (extensions.size() == 0) + { + logger.warn("No extensions defined for mimetype: " + mimetype); + } + } + + // make the collections read-only + this.mimetypes = Collections.unmodifiableList(this.mimetypes); + this.extensionsByMimetype = Collections.unmodifiableMap(this.extensionsByMimetype); + this.mimetypesByExtension = Collections.unmodifiableMap(this.mimetypesByExtension); + this.displaysByMimetype = Collections.unmodifiableMap(this.displaysByMimetype); + this.displaysByExtension = Collections.unmodifiableMap(this.displaysByExtension); + } + + /** + * Get the file extension associated with the mimetype. + * + * @param mimetype a valid mimetype + * @return Returns the default extension for the mimetype. Returns the + * {@link #MIMETYPE_BINARY binary} mimetype extension. + * + * @see #MIMETYPE_BINARY + * @see #EXTENSION_BINARY + */ + public String getExtension(String mimetype) + { + String extension = extensionsByMimetype.get(mimetype); + return (extension == null ? EXTENSION_BINARY : extension); + } + + /** + * Get the mimetype for the specified extension + * + * @param extension a valid file extension + * @return Returns a valid mimetype if found, or {@link #MIMETYPE_BINARY + * binary} as default. + */ + @Override + public String getMimetype(String extension) + { + return getMimetype(extension, MIMETYPE_BINARY); + } + + /** + * Get the mimetype for the specified extension or returns the supplied default if unknown. + */ + private String getMimetype(String extension, String defaultMimetype) + { + String mimetype = null; + if (extension != null) + { + extension = extension.toLowerCase(); + if (mimetypesByExtension.containsKey(extension)) + { + mimetype = mimetypesByExtension.get(extension); + } + } + return mimetype == null ? defaultMimetype : mimetype; + } + + public Map getDisplaysByExtension() + { + return displaysByExtension; + } + + public Map getDisplaysByMimetype() + { + return displaysByMimetype; + } + + public Map getExtensionsByMimetype() + { + return extensionsByMimetype; + } + + public List getMimetypes() + { + return mimetypes; + } + + public Map getMimetypesByExtension() + { + return mimetypesByExtension; + } + + public boolean isText(String mimetype) + { + return textMimetypes.contains(mimetype); + } + + /** + * Use Apache Tika to try to guess the type of the file. + * + * @return The mimetype, or null if we can't tell. + */ + private MediaType detectType(String filename, ContentReader reader) + { +// Metadata metadata = new Metadata(); +// if (filename != null) +// { +// metadata.add(Metadata.RESOURCE_NAME_KEY, filename); +// } + TikaInputStream inp = null; + try + { + if (reader != null) + { + if (reader instanceof FileContentReader) + { + try + { + inp = TikaInputStream.get(((FileContentReader) reader).getFile()); + } + catch (FileNotFoundException e) + { + logger.warn("No backing file found for ContentReader " + e); + return null; + } + } + else + { + inp = TikaInputStream.get(reader.getContentInputStream()); + } + } + return detectType(filename, inp); + } + finally + { + if (inp != null) + { + try + { + inp.close(); + } + catch (IOException e) + { + logger.error("Error while closing TikaInputStream.", e); + } + } + } + } + + private MediaType detectType(String filename, InputStream input) + { + TikaInputStream inp = null; + if (input != null) + { + inp = TikaInputStream.get(input); + } + return detectType(filename, inp); + } + + /** + * Use Apache Tika to try to guess the type of the file. + * + * @return The mimetype, or null if we can't tell. + */ + private MediaType detectType(String filename, TikaInputStream input) + { + Metadata metadata = new Metadata(); + if (filename != null) + { + metadata.add(Metadata.RESOURCE_NAME_KEY, filename); + } + + InputStream inp = null; + if (input != null) + { + inp = TikaInputStream.get(input); + } + + MediaType type; + try + { + type = detector.detect(inp, metadata); + logger.debug(input + " detected by Tika as being " + type.toString()); + } + catch (Exception e) + { + logger.warn("Error identifying content type of problem document", e); + return null; + } + finally + { + if (inp != null) + { + try + { + inp.close(); + } + catch (Exception e) + { + // noop + } + } + } + return type; + } + + /** + * Use Apache Tika to check if the mime type of the document really matches + * what it claims to be. This is typically used when a transformation or + * metadata extractions fails, and you want to know if someone has renamed a + * file and consequently it has the wrong mime type. + * + * @return Null if the mime type seems ok, otherwise the mime type it + * probably is + */ + public String getMimetypeIfNotMatches(ContentReader reader) + { + MediaType type = detectType(null, reader); + if (type == null) + { + // Tika doesn't know so we can't help, sorry... + return null; + } + + // Is it a good match? + if (type.toString().equals(reader.getMimetype())) { return null; } + + // Is it close? + MediaType claimed = MediaType.parse(reader.getMimetype()); + if (tikaConfig.getMediaTypeRegistry().isSpecializationOf(claimed, type) + || tikaConfig.getMediaTypeRegistry().isSpecializationOf(type, claimed)) + { + // Probably close enough + return null; + } + + // Check through known aliases of the type + SortedSet aliases = tikaConfig.getMediaTypeRegistry().getAliases(type); + for (MediaType alias : aliases) + { + String aliasType = alias.toString(); + if (aliasType.equals(claimed.toString())) + { + return null; + } + } + + // If we get here, then most likely the type is wrong + return type.toString(); + } + + /** + * Takes a guess at the mimetype based exclusively on the file extension, + * which can (and often is) wrong... + * + * @see #MIMETYPE_BINARY + */ + public String guessMimetype(String filename) + { + String mimetype = MIMETYPE_BINARY; + + // Extract the extension + if (filename != null && filename.length() > 0) + { + int index = filename.lastIndexOf('.'); + if (index > -1 && (index < filename.length() - 1)) + { + String extension = filename.substring(index + 1).toLowerCase(); + if (mimetypesByExtension.containsKey(extension)) + { + mimetype = mimetypesByExtension.get(extension); + } + } + } + return mimetype; + } + + /** + * Uses Tika to try to identify the mimetype of the file, falling back on + * {@link #guessMimetype(String)} for an extension based one if Tika can't + * help. + */ + public String guessMimetype(String filename, ContentReader reader) + { + // ALF-10813: MimetypeMap.guessMimetype consumes 30% of file upload time + // Let's only 'guess' if we need to + if (reader != null && reader.getMimetype() != null && !reader.getMimetype().equals(MimetypeMap.MIMETYPE_BINARY)) + { + // It was set to something other than the default. + // Possibly someone used this method before (like the UI does) or + // they just + // know what their files are. + return reader.getMimetype(); + } + InputStream input = (reader != null ? reader.getContentInputStream() : null); + return guessMimetype(filename, input); + } + + /** + * Uses Tika to try to identify the mimetype of the file, falling back on + * {@link #guessMimetype(String)} for an extension based one if Tika can't + * help. + */ + public String guessMimetype(String filename, InputStream input) + { + MediaType type = detectType(filename, input); + String filenameGuess = guessMimetype(filename); + + // If Tika doesn't know what the type is, or file is password protected, go with the filename one + if (type == null || MediaType.OCTET_STREAM.equals(type) || MIMETYPE_ENCRYPTED_OFFICE.equals(type.toString())) { return filenameGuess; } + + // If Tika has supplied a very generic type, go with the filename one, + // as it's probably a custom Text or XML format known only to Alfresco + if ((MediaType.TEXT_PLAIN.equals(type) || MediaType.APPLICATION_XML.equals(type)) && (! filenameGuess.equals(MIMETYPE_BINARY))) + { + return filenameGuess; + } + + // Alfresco doesn't support mimetype parameters + // Use the form of the mimetype without any + if (type.hasParameters()) + { + type = type.getBaseType(); + } + + // Not all the mimetypes we use are the Tika Canonical one. + // So, detect when this happens and use ours in preference + String tikaType = type.toString(); + if (mimetypes.contains(tikaType)) + { + // Alfresco and Tika agree! + return tikaType; + } + + // Check the aliases + SortedSet aliases = tikaConfig.getMediaTypeRegistry().getAliases(type); + for (MediaType alias : aliases) + { + String aliasType = alias.toString(); + if (mimetypes.contains(aliasType)) { return aliasType; } + } + + // If we get here, then Tika has identified something that + // Alfresco doesn't really know about. Just trust Tika on it + logger.info("Tika detected a type of " + tikaType + " for file " + filename + + " which Alfresco doesn't know about. Consider " + " adding that type to your configuration"); + return tikaType; + } + + /** + * Returns a collection of mimetypes ordered by extension. + * @param extension to restrict the collection to one entry + */ + public Collection getMimetypes(String extension) + { + Collection sourceMimetypes; + if (extension == null) + { + sourceMimetypes = getMimetypes(); + sourceMimetypes = sortMimetypesByExt(sourceMimetypes); + } + else + { + String mimetype = getMimetype(extension, null); + if (mimetype == null) + { + sourceMimetypes = Collections.emptySet(); + } + else + { + sourceMimetypes = Collections.singleton(mimetype); + } + } + return sourceMimetypes; + } + + /** + * Copies and sorts the supplied mimetypes by their file extensions + * @param mimetypes to be sorted + * @return a new List of sorted mimetypes + */ + private Collection sortMimetypesByExt(Collection mimetypes) + { + List result = new ArrayList(mimetypes); + for (int i=result.size()-1; i>= 0; i--) + { + result.set(i, getExtension(result.get(i))); + } + Collections.sort(result); + for (int i=result.size()-1; i>= 0; i--) + { + result.set(i, getMimetype(result.get(i))); + } + return result; + } +} diff --git a/src/main/java/org/alfresco/repo/content/encoding/ContentCharsetFinder.java b/src/main/java/org/alfresco/repo/content/encoding/ContentCharsetFinder.java new file mode 100644 index 0000000..b02b755 --- /dev/null +++ b/src/main/java/org/alfresco/repo/content/encoding/ContentCharsetFinder.java @@ -0,0 +1,117 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.content.encoding; + +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.List; + +import org.alfresco.encoding.CharactersetFinder; +import org.alfresco.service.cmr.repository.MimetypeService; + +/** + * Utility bean to guess the charset given a stream and a mimetype. + * + * @since 2.1 + * @author Derek Hulley + */ +public class ContentCharsetFinder +{ + private Charset defaultCharset = Charset.defaultCharset(); + private MimetypeService mimetypeService; + private List charactersetFinders; + + /** + * Override the system default charset. Where the characterset cannot be determined for + * a mimetype and input stream, this mimetype will be used. The default is 'UTF-8'. + * + * @param defaultCharset the default characterset + */ + public void setDefaultCharset(String defaultCharset) + { + this.defaultCharset = Charset.forName(defaultCharset); + } + + /** + * Set the mimetype service that will help determine if a particular mimetype can be + * treated as encoded text or not. + */ + public void setMimetypeService(MimetypeService mimetypeService) + { + this.mimetypeService = mimetypeService; + } + + /** + * Set the list of characterset finder to execute, in order, for text based content. + * @param charactersetFinders a list of finders + */ + public void setCharactersetFinders(List charactersetFinders) + { + this.charactersetFinders = charactersetFinders; + } + + /** + * Gets the characterset from the stream, if the mimetype is text and the text + * has enough information to give the encoding away. Otherwise, the default + * is returned. + * + * @param is a stream that will not be affected by the call, but must + * support marking + * @param mimetype the mimetype of the stream data - null if not known + * @return returns a characterset and never null + */ + public Charset getCharset(InputStream is, String mimetype) + { + if (mimetype == null) + { + return defaultCharset; + } + // Is it text? + if (!mimetypeService.isText(mimetype)) + { + return defaultCharset; + } + // Try the finders + Charset charset = null; + for (CharactersetFinder finder : charactersetFinders) + { + charset = finder.detectCharset(is); + if (charset != null) + { + break; + } + } + // Done + if (charset == null) + { + return defaultCharset; + } + else + { + return charset; + } + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/AbstractDictionaryRegistry.java b/src/main/java/org/alfresco/repo/dictionary/AbstractDictionaryRegistry.java new file mode 100644 index 0000000..74625eb --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/AbstractDictionaryRegistry.java @@ -0,0 +1,726 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.NamespaceException; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * + * @author sglover + * + */ +public abstract class AbstractDictionaryRegistry implements DictionaryRegistry +{ + protected static final Log logger = LogFactory.getLog(AbstractDictionaryRegistry.class); + + protected DictionaryDAO dictionaryDAO; + private Map> uriToModels = new ConcurrentHashMap>(0); + private Map compiledModels = new ConcurrentHashMap(0); + + // namespaces + private ReadWriteLock urisCacheRWLock = new ReentrantReadWriteLock(true); + private List urisCache = new ArrayList(20); + private Map prefixesCache = new ConcurrentHashMap(0); + + public AbstractDictionaryRegistry(DictionaryDAO dictionaryDAO) + { + this.dictionaryDAO = dictionaryDAO; + } + + @Override + public void clear() + { + setCompiledModels(new HashMap()); + setUriToModels(new HashMap>()); + } + + public Map> getUriToModels() + { + Map> ret = new HashMap<>(); + ret.putAll(uriToModels); // copy + return ret; + } + + private void setUriToModels(Map> uriToModels) + { + this.uriToModels = uriToModels; + } + + @Override + public Map getCompiledModels(boolean includeInherited) + { + return compiledModels; + } + + private void setCompiledModels(Map compiledModels) + { + this.compiledModels = compiledModels; + } + + public List getModelsForUri(String uri) + { + return getModelsForUriImpl(uri); + } + + @Override + public QName putModel(CompiledModel model) + { + return putModelImpl(model); + } + + @Override + public CompiledModel getModel(QName modelName) + { + CompiledModel model = getModelImpl(modelName); + if(model == null) + { + throw new DictionaryException("d_dictionary.model.err.no_model", modelName); + } + return model; + } + + @Override + public boolean modelExists(QName modelName) + { + CompiledModel model = getModelImpl(modelName); + return model != null; + } + + @Override + public void removeModel(QName modelName) + { + removeModelImpl(modelName); + } + + protected CompiledModel removeModelImpl(QName modelName) + { + CompiledModel compiledModel = compiledModels.get(modelName); + if (compiledModel != null) + { + // Remove the namespaces from the namespace service + M2Model model = compiledModel.getM2Model(); + for (M2Namespace namespace : model.getNamespaces()) + { + prefixesCache.remove(namespace.getPrefix()); + urisCacheRWLock.writeLock().lock(); + try + { + urisCache.remove(namespace.getUri()); + } + finally + { + urisCacheRWLock.writeLock().unlock(); + } + + List models = uriToModels.get(namespace.getUri()); + if(models != null) + { + models.remove(compiledModel); + } + } + + compiledModels.remove(modelName); + } + + return compiledModel; + } + + protected CompiledModel getModelImpl(QName modelName) + { + CompiledModel model = compiledModels.get(modelName); + return model; + } + + protected List getModelsForUriImpl(String uri) + { + List models = uriToModels.get(uri); + if(models == null) + { + models = Collections.emptyList(); + } + // defensive copy + return new ArrayList(models); + } + + protected void unmapUriToModel(String uri, CompiledModel model) + { + List models = uriToModels.get(uri); + if (models != null) + { + models.remove(model); + } + } + + protected void mapUriToModel(String uri, CompiledModel model) + { + List models = uriToModels.get(uri); + if (models == null) + { + models = new ArrayList(); + uriToModels.put(uri, models); + } + if (!models.contains(model)) + { + models.add(model); + } + } + + protected QName putModelImpl(CompiledModel model) + { + QName modelName = model.getModelDefinition().getName(); + + CompiledModel previousVersion = getModelImpl(modelName); + if (previousVersion != null) + { + for (M2Namespace namespace : previousVersion.getM2Model().getNamespaces()) + { + prefixesCache.remove(namespace.getPrefix()); + + urisCacheRWLock.writeLock().lock(); + try + { + urisCache.remove(namespace.getUri()); + } + finally + { + urisCacheRWLock.writeLock().unlock(); + } + unmapUriToModel(namespace.getUri(), previousVersion); + } + + for (M2Namespace importNamespace : previousVersion.getM2Model().getImports()) + { + unmapUriToModel(importNamespace.getUri(), previousVersion); + } + } + + // Create namespace definitions for new model + M2Model m2Model = model.getM2Model(); + for (M2Namespace namespace : m2Model.getNamespaces()) + { + addURI(namespace.getUri()); + addPrefix(namespace.getPrefix(), namespace.getUri()); + mapUriToModel(namespace.getUri(), model); + } + + for (M2Namespace importNamespace : m2Model.getImports()) + { + mapUriToModel(importNamespace.getUri(), model); + } + + compiledModels.put(modelName, model); + + return modelName; + } + + @Override + public AspectDefinition getAspect(QName aspectName) + { + return getAspectImpl(aspectName); + } + + protected AspectDefinition getAspectImpl(QName aspectName) + { + AspectDefinition aspect = null; + + if(aspectName != null) + { + List models = uriToModels.get(aspectName.getNamespaceURI()); + if(models != null && models.size() > 0) + { + for (CompiledModel model : models) + { + aspect = model.getAspect(aspectName); + if(aspect != null) + { + break; + } + } + } + } + + return aspect; + } + + @Override + public AssociationDefinition getAssociation(QName assocName) + { + return getAssociationImpl(assocName); + } + + protected AssociationDefinition getAssociationImpl(QName assocName) + { + AssociationDefinition assocDef = null; + + List models = getModelsForUri(assocName.getNamespaceURI()); + if(models != null && models.size() > 0) + { + for (CompiledModel model : models) + { + assocDef = model.getAssociation(assocName); + if(assocDef != null) + { + break; + } + } + } + + return assocDef; + } + + @Override + public ClassDefinition getClass(QName className) + { + return getClassImpl(className); + } + + protected ClassDefinition getClassImpl(QName className) + { + ClassDefinition classDef = null; + + List models = getModelsForUri(className.getNamespaceURI()); + if(models != null && models.size() > 0) + { + for (CompiledModel model : models) + { + classDef = model.getClass(className); + if (classDef != null) + { + break; + } + } + } + + return classDef; + } + + @Override + public PropertyDefinition getProperty(QName propertyName) + { + return getPropertyImpl(propertyName); + } + + protected PropertyDefinition getPropertyImpl(QName propertyName) + { + PropertyDefinition propDef = null; + + List models = getModelsForUri(propertyName.getNamespaceURI()); + if(models != null && models.size() > 0) + { + for (CompiledModel model : models) + { + propDef = model.getProperty(propertyName); + if(propDef != null) + { + break; + } + } + } + + return propDef; + } + + @Override + public TypeDefinition getType(QName typeName) + { + return getTypeImpl(typeName); + } + + protected TypeDefinition getTypeImpl(QName typeName) + { + TypeDefinition typeDef = null; + + if (typeName != null) + { + List models = getModelsForUri(typeName.getNamespaceURI()); + if(models != null && models.size() > 0) + { + for (CompiledModel model : models) + { + typeDef = model.getType(typeName); + if(typeDef != null) + { + break; + } + } + } + } + + return typeDef; + } + + @Override + public ConstraintDefinition getConstraint(QName constraintQName) + { + return getConstraintImpl(constraintQName); + } + + protected ConstraintDefinition getConstraintImpl(QName constraintQName) + { + ConstraintDefinition constraintDef = null; + + List models = getModelsForUri(constraintQName.getNamespaceURI()); + if(models != null && models.size() > 0) + { + for (CompiledModel model : models) + { + constraintDef = model.getConstraint(constraintQName); + if(constraintDef != null) + { + break; + } + } + } + + return constraintDef; + } + + @Override + public DataTypeDefinition getDataType(QName typeName) + { + return getDataTypeImp(typeName); + } + + protected DataTypeDefinition getDataTypeImp(QName typeName) + { + DataTypeDefinition dataType = null; + + if (typeName != null) + { + List models = getModelsForUri(typeName.getNamespaceURI()); + if(models != null && models.size() > 0) + { + for (CompiledModel model : models) + { + dataType = model.getDataType(typeName); + if(dataType != null) + { + break; + } + } + } + } + + return dataType; + } + + @SuppressWarnings("rawtypes") + @Override + public DataTypeDefinition getDataType(Class javaClass) + { + return getDataTypeImpl(javaClass); + } + + @SuppressWarnings("rawtypes") + protected DataTypeDefinition getDataTypeImpl(Class javaClass) + { + DataTypeDefinition dataTypeDef = null; + + if (javaClass != null) + { + for (CompiledModel model : getCompiledModels(true).values()) + { + dataTypeDef = model.getDataType(javaClass); + if(dataTypeDef != null) + { + break; + } + } + } + + return dataTypeDef; + } + + protected Map getPrefixesCacheImpl() + { + return prefixesCache; + } + + @Override + public Map getPrefixesCache() + { + return getPrefixesCacheImpl(); + } + + @Override + public List getUrisCache() + { + return getUrisCacheImpl(); + } + + protected List getUrisCacheImpl() + { + urisCacheRWLock.readLock().lock(); + try + { + return new ArrayList(urisCache); + } + finally + { + urisCacheRWLock.readLock().unlock(); + } + } + + @Override + public Collection getPrefixes(String URI) + { + return getPrefixesImpl(URI); + } + + protected Collection getPrefixesImpl(String URI) + { + Collection prefixes = new ArrayList(); + + for (String key : prefixesCache.keySet()) + { + String uri = prefixesCache.get(key); + if ((uri != null) && (uri.equals(URI))) + { + prefixes.add(key); + } + } + + return prefixes; + } + + @Override + public void addURI(String uri) + { + addURIImpl(uri); + } + + protected void addURIImpl(String uri) + { + if(hasURI(uri)) + { + throw new NamespaceException("URI " + uri + " has already been defined"); + } + + urisCacheRWLock.writeLock().lock(); + try + { + urisCache.add(uri); + } + finally + { + urisCacheRWLock.writeLock().unlock(); + } + } + + @Override + public boolean hasURI(String uri) + { + urisCacheRWLock.readLock().lock(); + try + { + return urisCache.contains(uri); + } + finally + { + urisCacheRWLock.readLock().unlock(); + } + } + + @Override + public void addPrefix(String prefix, String uri) + { + addPrefixImpl(prefix, uri); + } + + @Override + public boolean hasPrefix(String prefix) + { + return prefixesCache.containsKey(prefix); + } + + protected void addPrefixImpl(String prefix, String uri) + { + urisCacheRWLock.readLock().lock(); + try + { + if(!urisCache.contains(uri)) + { + throw new NamespaceException("Namespace URI " + uri + " does not exist"); + } + if (prefixesCache.containsKey(prefix)) + { + throw new NamespaceException( + String.format("Namespace prefix '%s' is already in use for URI '%s' so cannot be registered for URI '%s'", + prefix, + prefixesCache.get(prefix), + uri)); + } + prefixesCache.put(prefix, uri); + } + finally + { + urisCacheRWLock.readLock().unlock(); + } + } + + @Override + public void removeURI(String uri) + { + removeURIImpl(uri); + } + + @Override + public void removePrefix(String prefix) + { + removePrefixImpl(prefix); + } + + protected boolean removeURIImpl(String uri) + { + urisCacheRWLock.writeLock().lock(); + try + { + return urisCache.remove(uri); + } + finally + { + urisCacheRWLock.writeLock().unlock(); + } + } + + protected boolean removePrefixImpl(String prefix) + { + return (prefixesCache.remove(prefix) != null); + } + + @Override + public Collection getTypes(boolean includeInherited) + { + Collection types = new ArrayList(100); + for (QName model : getCompiledModels(includeInherited).keySet()) + { + for(TypeDefinition typeDef : getModel(model).getTypes()) + { + types.add(typeDef.getName()); + } + } + return types; + } + + @Override + public Collection getAssociations(boolean includeInherited) + { + Collection types = new ArrayList(100); + for (QName model : getCompiledModels(includeInherited).keySet()) + { + for(AssociationDefinition assocDef : getModel(model).getAssociations()) + { + types.add(assocDef.getName()); + } + } + return types; + } + + @Override + public Collection getAspects(boolean includeInherited) + { + Collection types = new ArrayList(100); + for (QName model : getCompiledModels(includeInherited).keySet()) + { + for(AspectDefinition aspectDef : getModel(model).getAspects()) + { + types.add(aspectDef.getName()); + } + } + return types; + } + + protected abstract void initImpl(); + + @Override + public void init() + { + initImpl(); + } + + protected abstract void removeImpl(); + + @Override + public void remove() + { + uriToModels.clear(); + compiledModels.clear(); + urisCacheRWLock.writeLock().lock(); + try + { + urisCache.clear(); + } + finally + { + urisCacheRWLock.writeLock().unlock(); + } + prefixesCache.clear(); + + removeImpl(); + } + + @Override + public boolean isModelInherited(QName modelName) + { + CompiledModel model = compiledModels.get(modelName); + return (model != null); + } + + @Override + public String getNamespaceURI(String prefix) + { + String namespaceURI = null; + + if(prefix != null) + { + namespaceURI = getPrefixesCache().get(prefix); + } + + return namespaceURI; + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/CompiledModel.java b/src/main/java/org/alfresco/repo/dictionary/CompiledModel.java new file mode 100644 index 0000000..9de963a --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/CompiledModel.java @@ -0,0 +1,459 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import static org.alfresco.service.cmr.dictionary.DictionaryException.DuplicateDefinitionException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.DynamicNamespacePrefixResolver; +import org.alfresco.service.namespace.NamespaceException; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * Compiled representation of a model definition. + * + * In this case, compiled means that + * a) all references between model items have been resolved + * b) inheritence of class features have been flattened + * c) overridden class features have been resolved + * + * A compiled model also represents a valid model. + * + * @author David Caruana + * + */ +public class CompiledModel implements ModelQuery +{ + + // Logger + private static final Log logger = LogFactory.getLog(DictionaryDAOImpl.class); + + private static final String ERR_COMPILE_MODEL_FAILURE = "d_dictionary.compiled_model.err.compile.failure"; + private static final String ERR_DUPLICATE_PROPERTY_TYPE = "d_dictionary.compiled_model.err.duplicate_property_type"; + private static final String ERR_DUPLICATE_TYPE = "d_dictionary.compiled_model.err.duplicate_type"; + private static final String ERR_DUPLICATE_ASPECT = "d_dictionary.compiled_model.err.duplicate_aspect"; + private static final String ERR_DUPLICATE_CONSTRAINT = "d_dictionary.compiled_model.err.duplicate_constraint"; + private static final String ERR_CYCLIC_REFERENCE = "d_dictionary.compiled_model.err.cyclic_ref"; + + private M2Model model; + private ModelDefinition modelDefinition; + private Map dataTypes = new HashMap(); + private Map classes = new HashMap(); + private Map types = new HashMap(); + private Map aspects = new HashMap(); + private Map properties = new HashMap(); + private Map associations = new HashMap(); + private Map constraints = new HashMap(); + + /** + * Construct + * + * @param model model definition + * @param dictionaryDAO dictionary DAO + * @param namespaceDAO namespace DAO + */ + /*package*/ CompiledModel(M2Model model, DictionaryDAO dictionaryDAO, NamespaceDAO namespaceDAO, boolean enableConstraintClassLoading) + { + try + { + // Phase 1: Construct model definitions from model entries + // resolving qualified names + this.model = model; + constructDefinitions(model, namespaceDAO, dictionaryDAO); + + // Phase 2: Resolve dependencies between model definitions + ModelQuery query = new DelegateModelQuery(this, dictionaryDAO); + resolveDependencies(query, namespaceDAO); + + // Phase 3: Resolve inheritance of values within class hierachy + NamespacePrefixResolver localPrefixes = createLocalPrefixResolver(model, namespaceDAO); + resolveInheritance(query, localPrefixes, constraints); + + // Phase 4: Resolve constraint dependencies + + for (ConstraintDefinition def : constraints.values()) + { + ((M2ConstraintDefinition)def).resolveDependencies(query, enableConstraintClassLoading); + } + + } + catch(Exception e) + { + throw new DictionaryException(ERR_COMPILE_MODEL_FAILURE, e, model.getName()); + } + } + + + /** + * @return the model definition + */ + public M2Model getM2Model() + { + return model; + } + + + /** + * Construct compiled definitions + * + * @param model model definition + * @param namespaceDAO namespace DAO + */ + private void constructDefinitions(M2Model model, NamespaceDAO namespaceDAO, DictionaryDAO dictionaryDAO) + { + NamespacePrefixResolver localPrefixes = createLocalPrefixResolver(model, namespaceDAO); + + // Construct Model Definition + modelDefinition = new M2ModelDefinition(model, localPrefixes, dictionaryDAO); + + // Construct Property Types + for (M2DataType propType : model.getPropertyTypes()) + { + M2DataTypeDefinition def = new M2DataTypeDefinition(modelDefinition, propType, localPrefixes); + if (dataTypes.containsKey(def.getName())) + { + throw new DuplicateDefinitionException(ERR_DUPLICATE_PROPERTY_TYPE, propType.getName()); + } + dataTypes.put(def.getName(), def); + } + + // Construct Type Definitions + for (M2Type type : model.getTypes()) + { + M2TypeDefinition def = new M2TypeDefinition(modelDefinition, type, localPrefixes, properties, associations); + if (classes.containsKey(def.getName())) + { + throw new DuplicateDefinitionException(ERR_DUPLICATE_TYPE, type.getName()); + } + classes.put(def.getName(), def); + types.put(def.getName(), def); + } + + // Construct Aspect Definitions + for (M2Aspect aspect : model.getAspects()) + { + M2AspectDefinition def = new M2AspectDefinition(modelDefinition, aspect, localPrefixes, properties, associations); + if (classes.containsKey(def.getName())) + { + throw new DuplicateDefinitionException(ERR_DUPLICATE_ASPECT, aspect.getName()); + } + classes.put(def.getName(), def); + aspects.put(def.getName(), def); + } + + // Construct Constraint Definitions + for (M2Constraint constraint : model.getConstraints()) + { + M2ConstraintDefinition def = new M2ConstraintDefinition(modelDefinition, null, constraint, localPrefixes); + QName qname = def.getName(); + if (constraints.containsKey(qname)) + { + throw new DuplicateDefinitionException(ERR_DUPLICATE_CONSTRAINT, constraint.getName()); + } + constraints.put(qname, def); + } + + } + + + /** + * Create a local namespace prefix resolver containing the namespaces defined and imported + * in the model + * + * @param model model definition + * @param namespaceDAO namespace DAO + * @return the local namespace prefix resolver + */ + private NamespacePrefixResolver createLocalPrefixResolver(M2Model model, NamespaceDAO namespaceDAO) + { + // Retrieve set of existing URIs for validation purposes + Collection uris = namespaceDAO.getURIs(); + + // Create a namespace prefix resolver based on imported and defined + // namespaces within the model + DynamicNamespacePrefixResolver prefixResolver = new DynamicNamespacePrefixResolver(null); + for (M2Namespace imported : model.getImports()) + { + String uri = imported.getUri(); + if (!uris.contains(uri)) + { + throw new NamespaceException("URI " + uri + " cannot be imported as it is not defined (with prefix " + imported.getPrefix()); + } + if(model.getNamespace(uri) != null) + { + throw new NamespaceException("URI " + uri + " cannot be imported as it is already contained in the model's namespaces"); + } + prefixResolver.registerNamespace(imported.getPrefix(), uri); + } + for (M2Namespace defined : model.getNamespaces()) + { + prefixResolver.registerNamespace(defined.getPrefix(), defined.getUri()); + } + return prefixResolver; + } + + + /** + * Resolve dependencies between model items + * + * @param query support for querying other items in model + */ + private void resolveDependencies(ModelQuery query, NamespaceDAO namespaceDAO) + { + NamespacePrefixResolver prefixResolver = createLocalPrefixResolver(model, namespaceDAO); + + for (DataTypeDefinition def : dataTypes.values()) + { + ((M2DataTypeDefinition)def).resolveDependencies(query); + } + for (ClassDefinition def : classes.values()) + { + ((M2ClassDefinition)def).resolveDependencies(query, prefixResolver, constraints); + } + } + + + /** + * Resolve class feature inheritence + * + * @param query support for querying other items in model + */ + private void resolveInheritance( + ModelQuery query, + NamespacePrefixResolver prefixResolver, + Map modelConstraints) + { + // Calculate order of class processing (root to leaf) + Map> order = new TreeMap>(); + for (ClassDefinition def : classes.values()) + { + // Calculate class depth in hierarchy + int depth = 0; + QName parentName = def.getParentName(); + Set traversedNodes = new HashSet(); + traversedNodes.add(def); + while (parentName != null) + { + ClassDefinition parentClass = getClass(parentName); + if (parentClass == null) + { + break; + } + if (traversedNodes.contains(parentClass)) + { + throw new DictionaryException(ERR_CYCLIC_REFERENCE, parentClass.getName(), model.getName()); + } + depth = depth +1; + traversedNodes.add(parentClass); + parentName = parentClass.getParentName(); + } + + // Map class to depth + List classes = order.get(depth); + if (classes == null) + { + classes = new ArrayList(); + order.put(depth, classes); + } + classes.add(def); + + if (logger.isTraceEnabled()) + { + logger.trace("Resolving inheritance: class " + def.getName() + " found at depth " + depth); + } + } + + // Resolve inheritance of each class + for (int depth = 0; depth < order.size(); depth++) + { + for (ClassDefinition def : order.get(depth)) + { + ((M2ClassDefinition)def).resolveInheritance(query, prefixResolver, modelConstraints); + } + } + } + + + /** + * @return the compiled model definition + */ + public ModelDefinition getModelDefinition() + { + return modelDefinition; + } + + + /** + * @return the compiled property types + */ + public Collection getDataTypes() + { + return dataTypes.values(); + } + + + /** + * @return the compiled types + */ + public Collection getTypes() + { + return types.values(); + } + + + /** + * @return the compiled aspects + */ + public Collection getAspects() + { + return aspects.values(); + } + + /** + * + * @return the compiled properties + */ + public Collection getProperties() + { + return properties.values(); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.impl.ModelQuery#getPropertyType(org.alfresco.repo.ref.QName) + */ + public DataTypeDefinition getDataType(QName name) + { + return dataTypes.get(name); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ModelQuery#getDataType(java.lang.Class) + */ + public DataTypeDefinition getDataType(Class javaClass) + { + for (DataTypeDefinition dataTypeDef : dataTypes.values()) + { + if (dataTypeDef.getJavaClassName().equals(javaClass.getName())) + { + return dataTypeDef; + } + } + return null; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.impl.ModelQuery#getType(org.alfresco.repo.ref.QName) + */ + public TypeDefinition getType(QName name) + { + return types.get(name); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.impl.ModelQuery#getAspect(org.alfresco.repo.ref.QName) + */ + public AspectDefinition getAspect(QName name) + { + return aspects.get(name); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.impl.ModelQuery#getClass(org.alfresco.repo.ref.QName) + */ + public ClassDefinition getClass(QName name) + { + return classes.get(name); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.impl.ModelQuery#getProperty(org.alfresco.repo.ref.QName) + */ + public PropertyDefinition getProperty(QName name) + { + return properties.get(name); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.impl.ModelQuery#getAssociation(org.alfresco.repo.ref.QName) + */ + public AssociationDefinition getAssociation(QName name) + { + return associations.get(name); + } + + /** + * @return the compiled associations + */ + public Collection getAssociations() + { + return associations.values(); + } + + /** + * @return the compiled constraints + */ + public Collection getConstraints() + { + return constraints.values(); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.impl.ModelQuery#getConstraint(QName) + */ + public ConstraintDefinition getConstraint(QName name) + { + return constraints.get(name); + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/CompiledModelsCache.java b/src/main/java/org/alfresco/repo/dictionary/CompiledModelsCache.java new file mode 100644 index 0000000..a193cbd --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/CompiledModelsCache.java @@ -0,0 +1,106 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.util.cache.AbstractAsynchronouslyRefreshedCache; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Asynchronously refreshed cache for dictionary models. + */ +public class CompiledModelsCache extends AbstractAsynchronouslyRefreshedCache +{ + private static Log logger = LogFactory.getLog(CompiledModelsCache.class); + + private DictionaryDAOImpl dictionaryDAO; + private TenantService tenantService; + + @Override + protected DictionaryRegistry buildCache(String tenantId) + { + if (tenantId == null) + { + tenantId = tenantService.getCurrentUserDomain(); + } + + final String finalTenant = tenantId; + return AuthenticationUtil.runAs(new RunAsWork() + { + public DictionaryRegistry doWork() throws Exception + { + return dictionaryDAO.initDictionaryRegistry(finalTenant); + } + }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantId)); + } + + /** + * @param tenantId the tenantId of cache that will be removed from live cache + * @return removed DictionaryRegistry + */ + public void remove(final String tenantId) + { + //TODO Should be reworked when ACE-2001 will be implemented + liveLock.writeLock().lock(); + try + { + DictionaryRegistry dictionaryRegistry = live.get(tenantId); + if (dictionaryRegistry != null) + { + live.remove(tenantId); + dictionaryRegistry.remove(); + + if (logger.isDebugEnabled()) + { + logger.debug("Removed dictionary register for tenant " + tenantId); + } + } + } + finally + { + liveLock.writeLock().unlock(); + } + } + + /** + * @param dictionaryDAO the dictionaryDAOImpl to set + */ + public void setDictionaryDAO(DictionaryDAOImpl dictionaryDAO) + { + this.dictionaryDAO = dictionaryDAO; + } + + /** + * @param tenantService the tenantService to set + */ + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/CoreDictionaryRegistryImpl.java b/src/main/java/org/alfresco/repo/dictionary/CoreDictionaryRegistryImpl.java new file mode 100644 index 0000000..b0e88c4 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/CoreDictionaryRegistryImpl.java @@ -0,0 +1,128 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.Map; + +import org.alfresco.service.namespace.QName; + +/** + * Core dictionary registry (holding core models initialised at bootstrap). + * + * @author sglover + * + */ +public class CoreDictionaryRegistryImpl extends AbstractDictionaryRegistry +{ + public CoreDictionaryRegistryImpl(DictionaryDAO dictionaryDAO) + { + super(dictionaryDAO); + } + + @Override + public String getTenantDomain() + { + return null; + } + + @Override + protected void initImpl() + { + long startTime = System.currentTimeMillis(); + + // populate the dictionary based on registered sources (only for core registry) + for (DictionaryListener dictionaryDeployer : dictionaryDAO.getDictionaryListeners()) + { + dictionaryDeployer.onDictionaryInit(); + } + + // notify registered listeners that dictionary has been initialised (population is complete) + for (DictionaryListener dictionaryListener : dictionaryDAO.getDictionaryListeners()) + { + dictionaryListener.afterDictionaryInit(); + } + + // Done + if (logger.isInfoEnabled()) + { + Map models = getCompiledModels(false); + logger.info("Init core dictionary: model count = "+(models != null ? models.size() : 0) + +" in "+(System.currentTimeMillis()-startTime)+" msecs ["+Thread.currentThread()+"]"); + } + } + + @Override + public void remove() + { + for(DictionaryListener listener : dictionaryDAO.getDictionaryListeners()) + { + listener.afterDictionaryDestroy(); + } + } + + @Override + protected QName putModelImpl(CompiledModel model) + { + // TODO disallow model overrides for the core dictionary + +// if(compiledModels.get(model.getModelDefinition().getName()) != null) +// { +// throw new AlfrescoRuntimeException("Cannot override existing model " + model.getModelDefinition().getName()); +// } +// +// for(M2Namespace namespace : model.getM2Model().getNamespaces()) +// { +// if(uriToModels.get(namespace.getUri()) != null) +// { +// throw new AlfrescoRuntimeException("Cannot override existing namespace " + namespace.getUri()); +// } +// } + + QName qname = super.putModelImpl(model); + +// if(dictionaryDAO.isContextRefreshed()) +// { +// for(DictionaryListener listener : dictionaryDAO.getDictionaryListeners()) +// { +// if(listener instanceof ExtendedDictionaryListener) +// { +// ((ExtendedDictionaryListener)listener).coreModelAdded(model); +// } +// } +// } + + return qname; + } + + @Override + public void removeImpl() + { + for(DictionaryListener listener : dictionaryDAO.getDictionaryListeners()) + { + listener.afterDictionaryDestroy(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/repo/dictionary/CustomModelDefinitionImpl.java b/src/main/java/org/alfresco/repo/dictionary/CustomModelDefinitionImpl.java new file mode 100644 index 0000000..7b86ed3 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/CustomModelDefinitionImpl.java @@ -0,0 +1,215 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ + +package org.alfresco.repo.dictionary; + +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.CustomModelDefinition; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.NamespaceDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.alfresco.service.namespace.QName; + +/** + * Read-only definition of a Custom Model + * + * @author Jamal Kaabi-Mofrad + */ +public class CustomModelDefinitionImpl implements CustomModelDefinition +{ + private final ModelDefinition m2ModelDefinition; + private final boolean active; + private final MessageLookup messageLookup; + private final Collection typeDefinitions; + private final Collection aspectDefinitions; + private final Collection modelDefinedConstraints; + + /* package */CustomModelDefinitionImpl(CompiledModel compiledModel, boolean active, MessageLookup messageLookup) + { + this.m2ModelDefinition = compiledModel.getModelDefinition(); + this.active = active; + this.messageLookup = messageLookup; + // compiledModel.getTypes(), getAspects and getConstraints are never null + this.typeDefinitions = new ArrayList<>(compiledModel.getTypes()); + this.aspectDefinitions = new ArrayList<>(compiledModel.getAspects()); + this.modelDefinedConstraints = removeInlineConstraints(compiledModel); + } + + /** + * Removes the inline constraints (i.e. defined within the property) from + * all constraints. The result will be constraints that have been defined + * within the model (Top level) itself. + * + * @param compiledModel the compiled model + * @return list of model defined constraints + */ + public static List removeInlineConstraints(CompiledModel compiledModel) + { + List modelConstraints = new ArrayList<>(); + + Set propertyConstraints = new HashSet<>(); + for(PropertyDefinition propDef : compiledModel.getProperties()) + { + if (propDef.getConstraints().size() > 0) + { + for (ConstraintDefinition propConst : propDef.getConstraints()) + { + propertyConstraints.add(propConst.getName()); + } + } + } + + for (ConstraintDefinition constraint : compiledModel.getConstraints()) + { + if (!propertyConstraints.contains(constraint.getName())) + { + modelConstraints.add(constraint); + } + } + + return modelConstraints; + } + + @Override + public String getDescription() + { + return getDescription(messageLookup); + } + + @Override + public boolean isActive() + { + return this.active; + } + + @Override + public String getAnalyserResourceBundleName() + { + return m2ModelDefinition.getAnalyserResourceBundleName(); + } + + @Override + public String getAuthor() + { + return m2ModelDefinition.getAuthor(); + } + + @Override + public long getChecksum(XMLBindingType xmlbindingtype) + { + return m2ModelDefinition.getChecksum(xmlbindingtype); + } + + @Override + public String getDescription(MessageLookup messagelookup) + { + return m2ModelDefinition.getDescription(messagelookup); + } + + @Override + public DictionaryDAO getDictionaryDAO() + { + return m2ModelDefinition.getDictionaryDAO(); + } + + @Override + public Collection getImportedNamespaces() + { + return m2ModelDefinition.getImportedNamespaces(); + } + + @Override + public QName getName() + { + return m2ModelDefinition.getName(); + } + + @Override + public Collection getNamespaces() + { + return m2ModelDefinition.getNamespaces(); + } + + @Override + public Date getPublishedDate() + { + return m2ModelDefinition.getPublishedDate(); + } + + @Override + public String getVersion() + { + return m2ModelDefinition.getVersion(); + } + + @Override + public boolean isNamespaceDefined(String uri) + { + return m2ModelDefinition.isNamespaceDefined(uri); + } + + @Override + public boolean isNamespaceImported(String uri) + { + return m2ModelDefinition.isNamespaceImported(uri); + } + + @Override + public void toXML(XMLBindingType xmlbindingtype, OutputStream xml) + { + m2ModelDefinition.toXML(xmlbindingtype, xml); + } + + @Override + public Collection getTypeDefinitions() + { + return Collections.unmodifiableCollection(typeDefinitions); + } + + @Override + public Collection getAspectDefinitions() + { + return Collections.unmodifiableCollection(aspectDefinitions); + } + + @Override + public Collection getModelDefinedConstraints() + { + return Collections.unmodifiableCollection(modelDefinedConstraints); + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/CustomModelsInfo.java b/src/main/java/org/alfresco/repo/dictionary/CustomModelsInfo.java new file mode 100644 index 0000000..63ecd59 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/CustomModelsInfo.java @@ -0,0 +1,69 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ + +package org.alfresco.repo.dictionary; + +/** + * A simple POJO to encapsulate the custom models' statistics information. + * + * @author Jamal Kaabi-Mofrad + */ +public class CustomModelsInfo +{ + private int numberOfActiveModels; + private int numberOfActiveTypes; + private int numberOfActiveAspects; + + public int getNumberOfActiveModels() + { + return this.numberOfActiveModels; + } + + public void setNumberOfActiveModels(int numberOfActiveModels) + { + this.numberOfActiveModels = numberOfActiveModels; + } + + public int getNumberOfActiveTypes() + { + return this.numberOfActiveTypes; + } + + public void setNumberOfActiveTypes(int numberOfActiveTypes) + { + this.numberOfActiveTypes = numberOfActiveTypes; + } + + public int getNumberOfActiveAspects() + { + return this.numberOfActiveAspects; + } + + public void setNumberOfActiveAspects(int numberOfActiveAspects) + { + this.numberOfActiveAspects = numberOfActiveAspects; + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/DelegateModelQuery.java b/src/main/java/org/alfresco/repo/dictionary/DelegateModelQuery.java new file mode 100644 index 0000000..3f1fd27 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/DelegateModelQuery.java @@ -0,0 +1,173 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.QName; + +/** + * Model query that delegates its search if itself cannot find the model + * item required. + * + * @author David Caruana + * + */ +/*package*/ class DelegateModelQuery implements ModelQuery +{ + + private ModelQuery query; + private ModelQuery delegate; + + + /** + * Construct + * + * @param query ModelQuery + * @param delegate ModelQuery + */ + /*package*/ DelegateModelQuery(ModelQuery query, ModelQuery delegate) + { + this.query = query; + this.delegate = delegate; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.impl.ModelQuery#getPropertyType(org.alfresco.repo.ref.QName) + */ + public DataTypeDefinition getDataType(QName name) + { + DataTypeDefinition def = query.getDataType(name); + if (def == null) + { + def = delegate.getDataType(name); + } + return def; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ModelQuery#getDataType(java.lang.Class) + */ + public DataTypeDefinition getDataType(Class javaClass) + { + DataTypeDefinition def = query.getDataType(javaClass); + if (def == null) + { + def = delegate.getDataType(javaClass); + } + return def; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.impl.ModelQuery#getType(org.alfresco.repo.ref.QName) + */ + public TypeDefinition getType(QName name) + { + TypeDefinition def = query.getType(name); + if (def == null) + { + def = delegate.getType(name); + } + return def; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.impl.ModelQuery#getAspect(org.alfresco.repo.ref.QName) + */ + public AspectDefinition getAspect(QName name) + { + AspectDefinition def = query.getAspect(name); + if (def == null) + { + def = delegate.getAspect(name); + } + return def; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.impl.ModelQuery#getClass(org.alfresco.repo.ref.QName) + */ + public ClassDefinition getClass(QName name) + { + ClassDefinition def = query.getClass(name); + if (def == null) + { + def = delegate.getClass(name); + } + return def; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.impl.ModelQuery#getProperty(org.alfresco.repo.ref.QName) + */ + public PropertyDefinition getProperty(QName name) + { + PropertyDefinition def = query.getProperty(name); + if (def == null) + { + def = delegate.getProperty(name); + } + return def; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.impl.ModelQuery#getAssociation(org.alfresco.repo.ref.QName) + */ + public AssociationDefinition getAssociation(QName name) + { + AssociationDefinition def = query.getAssociation(name); + if (def == null) + { + def = delegate.getAssociation(name); + } + return def; + } + + /* (non-Javadoc) + * @see ModelQuery#getConstraint(QName) + */ + public ConstraintDefinition getConstraint(QName name) + { + ConstraintDefinition def = query.getConstraint(name); + if (def == null) + { + def = delegate.getConstraint(name); + } + return def; + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/DictionaryBootstrap.java b/src/main/java/org/alfresco/repo/dictionary/DictionaryBootstrap.java new file mode 100644 index 0000000..256eefb --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/DictionaryBootstrap.java @@ -0,0 +1,232 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Properties; + +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; + + +/** + * Bootstrap Dictionary DAO with pre-defined models & message resources (from classpath) + * + * @author David Caruana, janv + * + */ +public class DictionaryBootstrap implements DictionaryListener +{ + // The list of models to bootstrap with + private List models = new ArrayList(); + + // The list of model resource bundles to bootstrap with + private List resourceBundles = new ArrayList(); + + // Dictionary DAO + private DictionaryDAO dictionaryDAO = null; + + // Tenant Service + private TenantService tenantService; + + private Properties globalProperties; + + // Logger + private static Log logger = LogFactory.getLog(DictionaryBootstrap.class); + + + /** + * Sets the Dictionary DAO + * + * @param dictionaryDAO DictionaryDAO + */ + public void setDictionaryDAO(DictionaryDAO dictionaryDAO) + { + this.dictionaryDAO = dictionaryDAO; + } + + /** + * Sets the Tenant Service + * + * @param tenantService TenantService + */ + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + /** + * Sets the global properties + * + * @param globalProperties + */ + public void setGlobalProperties(Properties globalProperties) + { + this.globalProperties = globalProperties; + } + + /** + * Sets the initial list of models to bootstrap with + * + * @param modelResources the model names + */ + public void setModels(List modelResources) + { + this.models = modelResources; + } + + /** + * Sets the initial list of models to bootstrap with + * + * @param labels the labels + */ + public void setLabels(List labels) + { + this.resourceBundles = labels; + } + + /** + * Bootstrap the Dictionary - register and populate + * + */ + public void bootstrap() + { + onDictionaryInit(); + initStaticMessages(); + + register(); + } + + /** + * Register with the Dictionary + */ + private void register() + { + dictionaryDAO.registerListener(this); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryListener#onInit() + */ + public void onDictionaryInit() + { + long startTime = System.currentTimeMillis(); + + if (logger.isTraceEnabled()) + { + logger.trace("onDictionaryInit: ["+Thread.currentThread()+"]"); + } + + // note: on first bootstrap will init empty dictionary + Collection modelsBefore = dictionaryDAO.getModels(true); // note: on first bootstrap will init empty dictionary + int modelsBeforeCnt = (modelsBefore != null ? modelsBefore.size() : 0); + + if ((tenantService == null) || (! tenantService.isTenantUser())) + { + // register models + for (String bootstrapModel : models) + { + InputStream modelStream = getClass().getClassLoader().getResourceAsStream(bootstrapModel); + if (modelStream == null) + { + throw new DictionaryException("d_dictionary.bootstrap.model_not_found", bootstrapModel); + } + try + { + M2Model model = M2Model.createModel(modelStream); + model.setConfigProperties(globalProperties); + + if (logger.isDebugEnabled()) + { + logger.debug("Loading model: "+model.getName()+" (from "+bootstrapModel+")"); + } + + dictionaryDAO.putModel(model); + } + catch(DictionaryException e) + { + throw new DictionaryException("d_dictionary.bootstrap.model_not_imported", e, bootstrapModel); + } + finally + { + try + { + modelStream.close(); + } + catch (IOException ioe) + { + logger.warn("Failed to close model input stream for '"+bootstrapModel+"': "+ioe); + } + } + } + + Collection modelsAfter = dictionaryDAO.getModels(true); + int modelsAfterCnt = (modelsAfter != null ? modelsAfter.size() : 0); + + if (logger.isDebugEnabled()) + { + logger.debug("Model count: before="+modelsBeforeCnt+", load="+models.size()+", after="+modelsAfterCnt+" in "+(System.currentTimeMillis()-startTime)+" msecs ["+Thread.currentThread()+"]"); + } + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryListener#afterInit() + */ + public void afterDictionaryInit() + { + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryListener#onDictionaryDestroy() + */ + public void afterDictionaryDestroy() + { + } + + /** + * Register the static resource bundles + */ + private void initStaticMessages() + { + // register messages + for (String resourceBundle : resourceBundles) + { + I18NUtil.registerResourceBundle(resourceBundle); + } + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/DictionaryComponent.java b/src/main/java/org/alfresco/repo/dictionary/DictionaryComponent.java new file mode 100644 index 0000000..3f1e3dc --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/DictionaryComponent.java @@ -0,0 +1,548 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.alfresco.repo.tenant.TenantDeployer; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.surf.util.ParameterCheck; + +/** + * Data Dictionary Service Implementation + * + * @author David Caruana + * @author sglover + */ +public class DictionaryComponent implements DictionaryService, TenantDeployer +{ + private DictionaryDAO dictionaryDAO; + private MessageLookup messageLookup; + + // TODO: Check passed arguments are valid + + /** + * Sets the Meta Model DAO + * + * @param dictionaryDAO dictionary DAO + */ + public void setDictionaryDAO(DictionaryDAO dictionaryDAO) + { + this.dictionaryDAO = dictionaryDAO; + } + + /** + * Sets the message lookup service + * + * @param messageLookup + * the message lookup service + */ + public void setMessageLookup(MessageLookup messageLookup) + { + this.messageLookup = messageLookup; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getAllModels() + */ + public Collection getAllModels() + { + return getAllModels(true); + } + + public Collection getAllModels(boolean includeInherited) + { + return dictionaryDAO.getModels(includeInherited); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getModel(org.alfresco.repo.ref.QName) + */ + public ModelDefinition getModel(QName model) + { + return dictionaryDAO.getModel(model); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getAllPropertyTypes() + */ + public Collection getAllDataTypes() + { + Collection propertyTypes = new ArrayList(); + for (QName model : getAllModels()) + { + propertyTypes.addAll(getDataTypes(model)); + } + return propertyTypes; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getPropertyTypes(org.alfresco.repo.ref.QName) + */ + public Collection getDataTypes(QName model) + { + Collection propertyTypes = dictionaryDAO.getDataTypes(model); + Collection qnames = new ArrayList(propertyTypes.size()); + for (DataTypeDefinition def : propertyTypes) + { + qnames.add(def.getName()); + } + return qnames; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getAllTypes() + */ + public Collection getAllTypes() + { + return getAllTypes(true); + } + + public Collection getAllTypes(boolean includeInherited) + { + return dictionaryDAO.getTypes(includeInherited); + } + + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.DictionaryService#getSubTypes(org.alfresco.service.namespace.QName, boolean) + */ + public Collection getSubTypes(QName superType, boolean follow) + { + return dictionaryDAO.getSubTypes(superType, follow); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getTypes(org.alfresco.repo.ref.QName) + */ + public Collection getTypes(QName model) + { + Collection types = dictionaryDAO.getTypes(model); + Collection qnames = new ArrayList(types.size()); + for (TypeDefinition def : types) + { + qnames.add(def.getName()); + } + return qnames; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getAllAspects() + */ + public Collection getAllAspects() + { + return getAllAspects(true); + } + + public Collection getAllAspects(boolean includeInherited) + { + return dictionaryDAO.getAspects(includeInherited); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getAllAssociations() + */ + public Collection getAllAssociations() + { + return getAllAssociations(true); + } + + public Collection getAllAssociations(boolean includeInherited) + { + return dictionaryDAO.getAssociations(includeInherited); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.DictionaryService#getSubAspects(org.alfresco.service.namespace.QName, boolean) + */ + public Collection getSubAspects(QName superAspect, boolean follow) + { + return dictionaryDAO.getSubAspects(superAspect, follow); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getAspects(org.alfresco.repo.ref.QName) + */ + public Collection getAspects(QName model) + { + Collection aspects = dictionaryDAO.getAspects(model); + Collection qnames = new ArrayList(aspects.size()); + for (AspectDefinition def : aspects) + { + qnames.add(def.getName()); + } + return qnames; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getAssociations(org.alfresco.repo.ref.QName) + */ + public Collection getAssociations(QName model) + { + Collection associations = dictionaryDAO.getAssociations(model); + Collection qnames = new ArrayList(associations.size()); + for (AssociationDefinition def : associations) + { + qnames.add(def.getName()); + } + return qnames; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#isSubClass(org.alfresco.repo.ref.QName, org.alfresco.repo.ref.QName) + */ + public boolean isSubClass(QName className, QName ofClassName) + { + // Validate arguments + ParameterCheck.mandatory("className", className); + ParameterCheck.mandatory("ofClassName", ofClassName); + ClassDefinition classDef = getClass(className); + if (classDef == null) + { + return false; + } + ClassDefinition ofClassDef = getClass(ofClassName); + if (ofClassDef == null) + { + return false; + } + + // Only check if both ends are either a type or an aspect + boolean subClassOf = false; + if (classDef.isAspect() == ofClassDef.isAspect()) + { + while (classDef != null) + { + if (classDef.equals(ofClassDef)) + { + subClassOf = true; + break; + } + + // No match yet, so go to parent class + QName parentClassName = classDef.getParentName(); + classDef = (parentClassName == null) ? null : getClass(parentClassName); + } + } + return subClassOf; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getPropertyType(org.alfresco.repo.ref.QName) + */ + public DataTypeDefinition getDataType(QName name) + { + return dictionaryDAO.getDataType(name); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.DictionaryService#getDataType(java.lang.Class) + */ + public DataTypeDefinition getDataType(Class javaClass) + { + return dictionaryDAO.getDataType(javaClass); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getType(org.alfresco.repo.ref.QName) + */ + public TypeDefinition getType(QName name) + { + return dictionaryDAO.getType(name); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getAspect(org.alfresco.repo.ref.QName) + */ + public AspectDefinition getAspect(QName name) + { + return dictionaryDAO.getAspect(name); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getClass(org.alfresco.repo.ref.QName) + */ + public ClassDefinition getClass(QName name) + { + return dictionaryDAO.getClass(name); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getAnonymousType(org.alfresco.repo.ref.QName, java.util.Collection) + */ + public TypeDefinition getAnonymousType(QName type, Collection aspects) + { + return dictionaryDAO.getAnonymousType(type, aspects); + } + + /** + * + * {@inheritDoc} + */ + public TypeDefinition getAnonymousType(QName name) + { + TypeDefinition typeDef = getType(name); + List aspects = typeDef.getDefaultAspects(true); + List aspectNames = new ArrayList(aspects.size()); + getMandatoryAspects(typeDef, aspectNames); + return getAnonymousType(typeDef.getName(), aspectNames); + } + + /** + * Gets a flattened list of all mandatory aspects for a given class + * + * @param classDef the class + * @param aspects a list to hold the mandatory aspects + */ + private void getMandatoryAspects(ClassDefinition classDef, List aspects) + { + for (AspectDefinition aspect : classDef.getDefaultAspects()) + { + QName aspectName = aspect.getName(); + if (!aspects.contains(aspectName)) + { + aspects.add(aspect.getName()); + getMandatoryAspects(aspect, aspects); + } + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getProperty(org.alfresco.repo.ref.QName, org.alfresco.repo.ref.QName) + */ + public PropertyDefinition getProperty(QName className, QName propertyName) + { + PropertyDefinition propDef = null; + ClassDefinition classDef = dictionaryDAO.getClass(className); + if (classDef != null) + { + Map propDefs = classDef.getProperties(); + propDef = propDefs.get(propertyName); + } + return propDef; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.DictionaryService#getPropertyDefs(org.alfresco.service.namespace.QName) + */ + public Map getPropertyDefs(QName className) + { + ClassDefinition classDef = dictionaryDAO.getClass(className); + if (classDef != null) + { + return classDef.getProperties(); + } + return null; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getProperty(org.alfresco.repo.ref.QName) + */ + public PropertyDefinition getProperty(QName propertyName) + { + return dictionaryDAO.getProperty(propertyName); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.DictionaryService#getAssociation(org.alfresco.repo.ref.QName) + */ + public AssociationDefinition getAssociation(QName associationName) + { + return dictionaryDAO.getAssociation(associationName); + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.DictionaryService#getAllProperties(org.alfresco.service.namespace.QName) + */ + public Collection getAllProperties(QName dataType) + { + Collection aspects = new HashSet(64); + for (QName model : getAllModels()) + { + aspects.addAll(getProperties(model, dataType)); + } + return aspects; + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.DictionaryService#getAllProperties(org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName) + */ + public Collection getProperties(QName model, QName dataType) + { + Collection propDefs = dictionaryDAO.getProperties(model, dataType); + HashSet props = new HashSet(propDefs.size()); + for(PropertyDefinition def : propDefs) + { + props.add(def.getName()); + } + return props; + + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.DictionaryService#getProperties(org.alfresco.service.namespace.QName) + */ + public Collection getProperties(QName model) + { + Collection propDefs = dictionaryDAO.getProperties(model); + HashSet props = new HashSet(propDefs.size()); + for(PropertyDefinition def : propDefs) + { + props.add(def.getName()); + } + return props; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.DictionaryService#getConstraint(org.alfresco.service.namespace.QName) + */ + public ConstraintDefinition getConstraint(QName constraintQName) + { + return dictionaryDAO.getConstraint(constraintQName); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.DictionaryService#getConstraints(org.alfresco.service.namespace.QName) + */ + public Collection getConstraints(QName model) + { + return dictionaryDAO.getConstraints(model); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.DictionaryService#getConstraints(org.alfresco.service.namespace.QName, boolean) + */ + public Collection getConstraints(QName model, boolean referenceableDefsOnly) + { + return dictionaryDAO.getConstraints(model, referenceableDefsOnly); + } + + public void init() + { + dictionaryDAO.init(); + } + + public void destroy() + { + dictionaryDAO.destroy(); + } + + public void onEnableTenant() + { + dictionaryDAO.reset(); // to initialise empty dictionary and re-populate + } + + public void onDisableTenant() + { + dictionaryDAO.destroy(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.i18n.MessageLookup#getMessage(java.lang.String) + */ + @Override + public String getMessage(String messageKey) + { + return messageLookup.getMessage(messageKey); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.i18n.MessageLookup#getMessage(java.lang.String, java.util.Locale) + */ + @Override + public String getMessage(String messageKey, Locale locale) + { + return messageLookup.getMessage(messageKey, locale); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.i18n.MessageLookup#getMessage(java.lang.String, java.lang.Object[]) + */ + @Override + public String getMessage(String messageKey, Object... params) + { + return messageLookup.getMessage(messageKey, params); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.i18n.MessageLookup#getMessage(java.lang.String, java.util.Locale, java.lang.Object[]) + */ + @Override + public String getMessage(String messageKey, Locale locale, Object... params) + { + return messageLookup.getMessage(messageKey, locale, params); + } + + @Override + public ModelDefinition getModelByNamespaceUri(String uri) + { + for(QName modelQname : dictionaryDAO.getModels()) + { + if(modelQname.getNamespaceURI().equals(uri)) + { + return getModel(modelQname); + } + } + return null; + } + +// @Override +// public Collection getCoreTypes() +// { +// return dictionaryDAO.getCoreTypes(); +// } +// +// @Override +// public Collection getCoreAspects() +// { +// return dictionaryDAO.getCoreAspects(); +// } +// +// @Override +// public Collection getCoreAssociations() +// { +// return dictionaryDAO.getCoreAssociations(); +// } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/DictionaryDAO.java b/src/main/java/org/alfresco/repo/dictionary/DictionaryDAO.java new file mode 100644 index 0000000..34cb5d7 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/DictionaryDAO.java @@ -0,0 +1,297 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.Collection; +import java.util.List; + +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.NamespaceDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.QName; + +/** + * Dictionary Data Access + * + * @author David Caruana, sglover + */ +public interface DictionaryDAO extends ModelQuery +{ + /** + * get DictionaryListener registered by calls to registerListener + * + * @see org.alfresco.repo.dictionary.DictionaryListener + * @return read only list of dictionary listeners + */ + List getDictionaryListeners(); + + DictionaryRegistry getDictionaryRegistry(String tenantDomain); + + boolean isContextRefreshed(); + + /** + * @return the models known by the dictionary + */ + /** + * @return the models known by the dictionary + */ + Collection getModels(); + + Collection getModels(boolean includeInherited); + + Collection getTypes(boolean includeInherited); + + Collection getAssociations(boolean includeInherited); + + Collection getAspects(boolean includeInherited); + + /** + * @param name + * the model to retrieve + * @return the named model definition + */ + ModelDefinition getModel(QName name); + + /** + * @param model + * the model to retrieve property types for + * @return the property types of the model + */ + Collection getDataTypes(QName model); + + /** + * @param model + * the model to retrieve types for + * @return the types of the model + */ + Collection getTypes(QName model); + + /** + * @param superType QName + * @param follow + * true => follow up the super-class hierarchy, false => + * immediate sub types only + */ + Collection getSubTypes(QName superType, boolean follow); + + /** + * @param model + * the model to retrieve aspects for + * @return the aspects of the model + */ + Collection getAspects(QName model); + + /** + * @param model + * the model to retrieve associations for + * @return the associations of the model + */ + Collection getAssociations(QName model); + + /** + * @param superAspect QName + * @param follow + * true => follow up the super-class hierarchy, false => + * immediate sub aspects only + */ + Collection getSubAspects(QName superAspect, boolean follow); + + /** + * @param model + * the model for which to get properties for + * @return the properties of the model + */ + Collection getProperties(QName model); + + /** + * Construct an anonymous type that combines a primary type definition and + * and one or more aspects + * + * @param type + * the primary type + * @param aspects + * the aspects to combine + * @return the anonymous type definition + */ + TypeDefinition getAnonymousType(QName type, Collection aspects); + + /** + * Adds a model to the dictionary. The model is compiled and validated. + * + * @param model + * the model to add + * @return QName name of model + */ + QName putModel(M2Model model); + + // QName putCustomModel(M2Model model); + + /** + * Adds a model to the dictionary. The model is compiled and validated. + * Constraints are not loaded. + * + * This method should only be used to load models where the enforcement of + * constraints is never required. For example, SOLR read only use of the + * index where contraints are not required and thier definitions may not be + * available. + * + * @param model + * the model to add + * @return QName name of model + */ + QName putModelIgnoringConstraints(M2Model model); + + /** + * Removes a model from the dictionary. The types and aspect in the model + * will no longer be available. + * + * @param model + * the qname of the model to remove + */ + void removeModel(QName model); + + /** + * Get all properties for the model and that are of the given data type. If + * dataType is null then the all properties will be returned. + * + * @param modelName + * the name of the model + * @param dataType + * null to get all properties + * @return the properties associated with the model + */ + Collection getProperties(QName modelName, QName dataType); + + /** + * Get all properties for all models of the given data type. + * + * @param dataType QName + */ + Collection getPropertiesOfDataType(QName dataType); + + /** + * @param modelName + * the model to retrieve namespaces for + * @return the namespaces of the model + */ + Collection getNamespaces(QName modelName); + + /** + * @param model + * the model to retrieve constraint defs (including property + * constaint refs) + * @return the constraints of the model + */ + Collection getConstraints(QName model); + + /** + * @param model + * the model to retrieve constraint defs (optionally only + * referenceable constraints) + * @return the constraints of the model + */ + Collection getConstraints(QName model, + boolean referenceableDefsOnly); + + /** + * Return diffs between input model and model in the Dictionary. + * + * If the input model does not exist in the Dictionary then no diffs will be + * returned. + * + * @param model M2Model + * @return model diffs (if any) + */ + List diffModel(M2Model model); + + List diffModelIgnoringConstraints(M2Model model); + + /** + * Register listener with the Dictionary + *

+ * This method is deprecated, use {@link #registerListener(DictionaryListener dictionaryListener)} instead. + *

+ * @param dictionaryListener + */ + @Deprecated + void register(DictionaryListener dictionaryListener); + + /** + * + * Register listener with the Dictionary + * + * @param dictionaryListener DictionaryListener + */ + void registerListener(DictionaryListener dictionaryListener); + + /** + * Reset the Dictionary for the current tenant. + * The current dictionary will be discarded and reloaded before the method returns + * i.e. upon return the dictionary will be current. + */ + void reset(); + + /** + * Initialise a reload of the dictionary for the current tenant. The current version of + * the dictionary will be accessible during this call, however it will only return once + * the dictionary has undergone a reload for the current tenant. + */ + void init(); + + /** + * Destroy the Dictionary. After this call, there will be no dictionary available for the current + * tenant; reloading will be done lazily as required. + *

+ * WARNING: This method can cause 'stutter' on user threads as they wait for + * the dictionary to reload. It is safer to call {@link #init()}, which will also rebuild the + * dictionary but will not destroy the old one, thereby allowing other threads to continue + * operating. + */ + void destroy(); + + // MT-specific + boolean isModelInherited(QName name); + + /** + * @return String + */ + String getDefaultAnalyserResourceBundleName(); + + /** + * @return ClassLoader + */ + ClassLoader getResourceClassLoader(); + + /** + * @param resourceClassLoader ClassLoader + */ + void setResourceClassLoader(ClassLoader resourceClassLoader); +} diff --git a/src/main/java/org/alfresco/repo/dictionary/DictionaryDAOImpl.java b/src/main/java/org/alfresco/repo/dictionary/DictionaryDAOImpl.java new file mode 100644 index 0000000..200eb6b --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/DictionaryDAOImpl.java @@ -0,0 +1,1054 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.NamespaceDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; + +/** + * Default implementation of the Dictionary. + * + * @author David Caruana, janv, sglover + * + */ +// TODO deal with destroy of core dictionary registry i.e. do we remove all +// tenant dictionary registries too? +public class DictionaryDAOImpl implements DictionaryDAO, NamespaceDAO, + ApplicationListener +{ + // Tenant Service + private TenantService tenantService; + + // used to reset the cache + private ThreadLocal> dictionaryRegistryThreadLocal = new ThreadLocal>(); + + // Internal cache (clusterable) + private CompiledModelsCache dictionaryRegistryCache; + + // Static list of registered dictionary listeners + private List dictionaryListeners = new ArrayList(); + private ReadWriteLock dictionaryListenersLock = new ReentrantReadWriteLock(); + + // Logger + private static Log logger = LogFactory.getLog(DictionaryDAO.class); + + private String defaultAnalyserResourceBundleName; + + private ClassLoader resourceClassLoader; + + // inject dependencies + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + public void setDictionaryRegistryCache( + CompiledModelsCache dictionaryRegistryCache) + { + this.dictionaryRegistryCache = dictionaryRegistryCache; + } + + @Override + public String getDefaultAnalyserResourceBundleName() + { + return defaultAnalyserResourceBundleName; + } + + public void setDefaultAnalyserResourceBundleName( + String defaultAnalyserResourceBundleName) + { + this.defaultAnalyserResourceBundleName = defaultAnalyserResourceBundleName; + } + + /** + * Construct + * + */ + public DictionaryDAOImpl() + { + } + + /** + * Register listener with the Dictionary + *

+ * This method is deprecated, use {@link #registerListener(DictionaryListener dictionaryListener)} instead. + *

+ * @param dictionaryListener + */ + @Override + @Deprecated + public void register(DictionaryListener dictionaryListener) + { + registerListener(dictionaryListener); + } + + /** + * Register with the Dictionary + */ + @Override + public void registerListener(DictionaryListener dictionaryListener) + { + this.dictionaryListenersLock.writeLock().lock(); + try + { + if (!dictionaryListeners.contains(dictionaryListener)) + { + dictionaryListeners.add(dictionaryListener); + } + } + finally + { + this.dictionaryListenersLock.writeLock().unlock(); + } + } + + @Override + public List getDictionaryListeners() + { + // need to hold read lock here + this.dictionaryListenersLock.readLock().lock(); + try + { + return new ArrayList(dictionaryListeners); + } + finally + { + this.dictionaryListenersLock.readLock().unlock(); + } + } + + private Map getThreadLocal() + { + Map map = dictionaryRegistryThreadLocal + .get(); + if (map == null) + { + map = new HashMap(); + dictionaryRegistryThreadLocal.set(map); + } + return map; + } + + private DictionaryRegistry createCoreDictionaryRegistry() + { + DictionaryRegistry dictionaryRegistry = new CoreDictionaryRegistryImpl( + this); + getThreadLocal().put("", dictionaryRegistry); + dictionaryRegistry.init(); + getThreadLocal().remove(""); + return dictionaryRegistry; + } + + private DictionaryRegistry createTenantDictionaryRegistry( + final String tenant) + { + DictionaryRegistry result = AuthenticationUtil.runAs( + new RunAsWork() + { + public DictionaryRegistry doWork() + { + DictionaryRegistry dictionaryRegistry = new TenantDictionaryRegistryImpl( + DictionaryDAOImpl.this, tenant); + getThreadLocal().put(tenant, dictionaryRegistry); + dictionaryRegistry.init(); + getThreadLocal().remove(tenant); + return dictionaryRegistry; + } + }, tenantService.getDomainUser( + AuthenticationUtil.getSystemUserName(), tenant)); + + return result; + } + + @Override + public void init() + { + String tenant = tenantService.getCurrentUserDomain(); + + dictionaryRegistryCache.forceInChangesForThisUncommittedTransaction(tenant); + + if (logger.isDebugEnabled()) + { + logger.debug("Triggered immediate reload of dictionary for tenant " + tenant); + } + } + + @Override + public void destroy() + { + String tenant = tenantService.getCurrentUserDomain(); + + // TODO Should be reworked when ACE-2001 will be implemented + dictionaryRegistryCache.remove(tenant); + dictionaryRegistryCache.refresh(tenant); + + if (logger.isDebugEnabled()) + { + logger.debug("Dictionary destroyed for tenant " + tenant); + } + } + + @Override + public void reset() + { + String tenant = tenantService.getCurrentUserDomain(); + if (logger.isDebugEnabled()) + { + logger.debug("Resetting dictionary for tenant " + tenant); + } + + destroy(); + // Ensure that we have a dictionary available right now + getDictionaryRegistry(tenant); + + if (logger.isDebugEnabled()) + { + logger.debug("Dictionary reset complete for tenant " + tenant); + } + } + + @Override + public QName putModel(M2Model model) + { + // the core registry is not yet initialised so put it in the core registry + QName ret = putModelImpl(model, true); + return ret; + } + + @Override + public QName putModelIgnoringConstraints(M2Model model) + { + return putModelImpl(model, false); + } + + private QName putModelImpl(M2Model model, boolean enableConstraintClassLoading) + { + // Compile model definition + CompiledModel compiledModel = model.compile(this, this, + enableConstraintClassLoading); + QName modelName = compiledModel.getModelDefinition().getName(); + + getTenantDictionaryRegistry().putModel(compiledModel); + + if (logger.isTraceEnabled()) + { + logger.trace("Registered core model: " + + modelName.toPrefixString(this)); + for (M2Namespace namespace : model.getNamespaces()) + { + logger.trace("Registered core namespace: '" + + namespace.getUri() + "' (prefix '" + + namespace.getPrefix() + "')"); + } + } + + return modelName; + } + + /** + * @see org.alfresco.repo.dictionary.DictionaryDAO#removeModel(org.alfresco.service.namespace.QName) + */ + public void removeModel(QName modelName) + { + getTenantDictionaryRegistry().removeModel(modelName); + } + + private DictionaryRegistry getTenantDictionaryRegistry() + { + String tenantDomain = tenantService.getCurrentUserDomain(); + return getDictionaryRegistry(tenantDomain); + } + + /** + * @param modelName + * the model name + * @return the compiled model of the given name + */ + public CompiledModel getCompiledModel(QName modelName) + { + return getTenantDictionaryRegistry().getModel(modelName); + } + + @Override + public DataTypeDefinition getDataType(QName typeName) + { + DataTypeDefinition dataTypeDef = null; + + if (typeName != null) + { + dataTypeDef = getTenantDictionaryRegistry().getDataType(typeName); + } + + return dataTypeDef; + } + + @SuppressWarnings("rawtypes") + @Override + public DataTypeDefinition getDataType(Class javaClass) + { + return getTenantDictionaryRegistry().getDataType(javaClass); + } + + @Override + public Collection getDataTypes(QName modelName) + { + CompiledModel model = getCompiledModel(modelName); + return model.getDataTypes(); + } + + @Override + public TypeDefinition getType(QName typeName) + { + TypeDefinition typeDef = null; + + if (typeName != null) + { + typeDef = getTenantDictionaryRegistry().getType(typeName); + } + + return typeDef; + } + + @Override + public Collection getSubTypes(QName superType, boolean follow) + { + // note: could be optimised further, if compiled into the model + + // Get all types (with parent type) for all models + Map allTypesAndParents = new HashMap(); // name, + // parent + + for (CompiledModel model : getCompiledModels(true).values()) + { + for (TypeDefinition type : model.getTypes()) + { + allTypesAndParents.put(type.getName(), type.getParentName()); + } + } + + // Get sub types + HashSet subTypes = new HashSet(); + for (QName type : allTypesAndParents.keySet()) + { + if (follow) + { + // all sub types + QName current = type; + while ((current != null) && !current.equals(superType)) + { + current = allTypesAndParents.get(current); // get parent + } + if (current != null) + { + subTypes.add(type); + } + } + else + { + // immediate sub types only + QName typesSuperType = allTypesAndParents.get(type); + if (typesSuperType != null && typesSuperType.equals(superType)) + { + subTypes.add(type); + } + } + + } + return subTypes; + } + + @Override + public AspectDefinition getAspect(QName aspectName) + { + AspectDefinition aspectDef = null; + + if (aspectName != null) + { + aspectDef = getTenantDictionaryRegistry().getAspect(aspectName); + } + + return aspectDef; + } + + @Override + public Collection getSubAspects(QName superAspect, boolean follow) + { + // note: could be optimised further, if compiled into the model + + // Get all aspects (with parent aspect) for all models + Map allAspectsAndParents = new HashMap(); // name, + // parent + + for (CompiledModel model : getCompiledModels(true).values()) + { + for (AspectDefinition aspect : model.getAspects()) + { + allAspectsAndParents.put(aspect.getName(), + aspect.getParentName()); + } + } + + // Get sub aspects + HashSet subAspects = new HashSet(); + for (QName aspect : allAspectsAndParents.keySet()) + { + if (follow) + { + // all sub aspects + QName current = aspect; + while ((current != null) && !current.equals(superAspect)) + { + current = allAspectsAndParents.get(current); // get parent + } + if (current != null) + { + subAspects.add(aspect); + } + } + else + { + // immediate sub aspects only + QName typesSuperAspect = allAspectsAndParents.get(aspect); + if (typesSuperAspect != null + && typesSuperAspect.equals(superAspect)) + { + subAspects.add(aspect); + } + } + } + return subAspects; + } + + @Override + public ClassDefinition getClass(QName className) + { + ClassDefinition classDef = null; + + if (className != null) + { + classDef = getTenantDictionaryRegistry().getClass(className); + } + + return classDef; + } + + @Override + public PropertyDefinition getProperty(QName propertyName) + { + PropertyDefinition propertyDef = null; + + if (propertyName != null) + { + propertyDef = getTenantDictionaryRegistry().getProperty( + propertyName); + } + + return propertyDef; + } + + @Override + public ConstraintDefinition getConstraint(QName constraintQName) + { + ConstraintDefinition constraintDef = null; + + if (constraintQName != null) + { + constraintDef = getTenantDictionaryRegistry().getConstraint( + constraintQName); + } + + return constraintDef; + } + + @Override + public AssociationDefinition getAssociation(QName assocName) + { + return getTenantDictionaryRegistry().getAssociation(assocName); + } + + public Collection getAssociations(QName modelName) + { + CompiledModel model = getCompiledModel(modelName); + return model.getAssociations(); + } + + public Collection getModels(boolean includeInherited) + { + // get all models - including inherited models, if applicable + return getCompiledModels(includeInherited).keySet(); + } + + @Override + public Collection getModels() + { + // get all models - including inherited models, if applicable + return getModels(true); + } + + public Collection getTypes(boolean includeInherited) + { + return getTenantDictionaryRegistry().getTypes(includeInherited); + } + + public Collection getAssociations(boolean includeInherited) + { + return getTenantDictionaryRegistry().getAssociations(includeInherited); + } + + public Collection getAspects(boolean includeInherited) + { + return getTenantDictionaryRegistry().getAspects(includeInherited); + } + + // MT-specific + public boolean isModelInherited(QName modelName) + { + return getTenantDictionaryRegistry().isModelInherited(modelName); + } + + private Map getCompiledModels(boolean includeInherited) + { + return getTenantDictionaryRegistry().getCompiledModels(includeInherited); + } + + @Override + public ModelDefinition getModel(QName name) + { + CompiledModel model = getCompiledModel(name); + return model.getModelDefinition(); + } + + @Override + public Collection getTypes(QName modelName) + { + CompiledModel model = getCompiledModel(modelName); + return model.getTypes(); + } + + @Override + public Collection getAspects(QName modelName) + { + CompiledModel model = getCompiledModel(modelName); + return model.getAspects(); + } + + @Override + public TypeDefinition getAnonymousType(QName type, Collection aspects) + { + TypeDefinition typeDef = getType(type); + if (typeDef == null) + { + throw new DictionaryException( + "d_dictionary.model.err.type_not_found", type); + } + Collection aspectDefs = new ArrayList(); + if (aspects != null) + { + for (QName aspect : aspects) + { + AspectDefinition aspectDef = getAspect(aspect); + if (aspectDef == null) + { + throw new DictionaryException( + "d_dictionary.model.err.aspect_not_found", aspect); + } + aspectDefs.add(aspectDef); + } + } + return new M2AnonymousTypeDefinition(typeDef, aspectDefs); + } + + @Override + public Collection getProperties(QName modelName) + { + CompiledModel model = getCompiledModel(modelName); + return model.getProperties(); + } + + @Override + public Collection getProperties(QName modelName, + QName dataType) + { + HashSet properties = new HashSet(); + + Collection props = getProperties(modelName); + for (PropertyDefinition prop : props) + { + if ((dataType == null) + || prop.getDataType().getName().equals(dataType)) + { + properties.add(prop); + } + } + return properties; + } + + @Override + public Collection getPropertiesOfDataType(QName dataType) + { + Collection properties = new HashSet(); + + Collection modelNames = getModels(); + for (QName modelName : modelNames) + { + properties.addAll(getProperties(modelName, dataType)); + } + + return properties; + } + + @Override + public Collection getNamespaces(QName modelName) + { + CompiledModel model = getCompiledModel(modelName); + ModelDefinition modelDef = model.getModelDefinition(); + return modelDef.getNamespaces(); + } + + @Override + public Collection getConstraints(QName modelName) + { + return getConstraints(modelName, false); + } + + public Collection getConstraints(QName modelName, + boolean referenceableDefsOnly) + { + CompiledModel model = getCompiledModel(modelName); + if (referenceableDefsOnly) + { + return getReferenceableConstraintDefs(model); + } + else + { + return model.getConstraints(); + } + } + + private Collection getReferenceableConstraintDefs( + CompiledModel model) + { + Collection conDefs = model.getConstraints(); + Collection propDefs = model.getProperties(); + for (PropertyDefinition propDef : propDefs) + { + for (ConstraintDefinition conDef : propDef.getConstraints()) + { + conDefs.remove(conDef); + } + } + + return conDefs; + } + + // re-entrant (eg. via reset) + @Override + public DictionaryRegistry getDictionaryRegistry(String tenantDomain) + { + DictionaryRegistry dictionaryRegistry = null; + + if (tenantDomain == null) + { + throw new AlfrescoRuntimeException("Tenant must be set"); + } + + // check threadlocal first - return if set + dictionaryRegistry = getThreadLocal().get(tenantDomain); + if (dictionaryRegistry == null) + { + dictionaryRegistry = dictionaryRegistryCache.get(tenantDomain); + } + + return dictionaryRegistry; + } + + /** + * For cache use only. + * + * @param tenantDomain String + * @return constructed DictionaryRegistry + */ + public DictionaryRegistry initDictionaryRegistry(final String tenantDomain) + { + return AuthenticationUtil.runAs( + new RunAsWork() + { + public DictionaryRegistry doWork() + { + DictionaryRegistry dictionaryRegistry = null; + if (tenantDomain.equals(TenantService.DEFAULT_DOMAIN)) + { + dictionaryRegistry = createCoreDictionaryRegistry(); + } + else + { + dictionaryRegistry = createTenantDictionaryRegistry(tenantDomain); + } + + getThreadLocal().put(tenantDomain, dictionaryRegistry); + dictionaryRegistry.init(); + getThreadLocal().remove(tenantDomain); + + return dictionaryRegistry; + } + }, + tenantService.getDomainUser( + AuthenticationUtil.getSystemUserName(), tenantDomain)); + } + + /** + * Return diffs between input model and model in the Dictionary. + * + * If the input model does not exist in the Dictionary then no diffs will be + * returned. + * + * @param model M2Model + * @return model diffs (if any) + */ + public List diffModel(M2Model model) + { + return diffModel(model, true); + } + + public List diffModelIgnoringConstraints(M2Model model) + { + return diffModel(model, false); + } + + /** + * Return diffs between input model and model in the Dictionary. + * + * If the input model does not exist in the Dictionary then no diffs will be + * returned. + * + * @param model M2Model + * @param enableConstraintClassLoading boolean + * @return model diffs (if any) + */ + public List diffModel(M2Model model, + boolean enableConstraintClassLoading) + { + // Compile model definition + CompiledModel compiledModel = model.compile(this, this, + enableConstraintClassLoading); + QName modelName = compiledModel.getModelDefinition().getName(); + + CompiledModel previousVersion = null; + try + { + previousVersion = getCompiledModel(modelName); + } + catch (DictionaryException e) + { + // ignore missing model, there's no need to warn about this. + logger.debug(e); + } + + if (previousVersion == null) + { + return new ArrayList(0); + } + else + { + return diffModel(previousVersion, compiledModel); + } + } + + /** + * Return diffs between two compiled models. + * + * note: - checks classes (types & aspects) for incremental updates - checks + * properties for incremental updates, but does not include the diffs - + * checks assocs & child assocs for incremental updates, but does not + * include the diffs - incremental updates include changes in + * title/description, property default value, etc - ignores changes in model + * definition except name (ie. title, description, author, published date, + * version are treated as an incremental update) + * + * TODO - imports - namespace - datatypes - constraints (including property + * constraints - references and inline) + * + * @param previousVersion CompiledModel + * @param model CompiledModel + * @return model diffs (if any) + */ + /* package */List diffModel(CompiledModel previousVersion, + CompiledModel model) + { + List M2ModelDiffs = new ArrayList(); + + if (previousVersion != null) + { + Collection previousTypes = previousVersion + .getTypes(); + Collection previousAspects = previousVersion + .getAspects(); + Collection previousConDefs = getReferenceableConstraintDefs(previousVersion); + Collection previousImportedNamespaces = previousVersion.getModelDefinition().getImportedNamespaces(); + + if (model == null) + { + // delete model + for (TypeDefinition previousType : previousTypes) + { + M2ModelDiffs.add(new M2ModelDiff(previousType.getName(), + M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_DELETED)); + } + for (AspectDefinition previousAspect : previousAspects) + { + M2ModelDiffs.add(new M2ModelDiff(previousAspect.getName(), + M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_DELETED)); + } + for (ConstraintDefinition previousConDef : previousConDefs) + { + M2ModelDiffs.add(new M2ModelDiff(previousConDef.getName(), + M2ModelDiff.TYPE_CONSTRAINT, + M2ModelDiff.DIFF_DELETED)); + } + } + else + { + // update model + Collection types = model.getTypes(); + Collection aspects = model.getAspects(); + Collection conDefs = getReferenceableConstraintDefs(model); + Collection importedNamespaces = model.getModelDefinition().getImportedNamespaces(); + + if (previousTypes.size() != 0) + { + M2ModelDiffs.addAll(M2ClassDefinition.diffClassLists( + new ArrayList(previousTypes), + new ArrayList(types), + M2ModelDiff.TYPE_TYPE)); + } + else + { + for (TypeDefinition type : types) + { + M2ModelDiffs + .add(new M2ModelDiff(type.getName(), + M2ModelDiff.TYPE_TYPE, + M2ModelDiff.DIFF_CREATED)); + } + } + + if (previousAspects.size() != 0) + { + M2ModelDiffs.addAll(M2ClassDefinition.diffClassLists( + new ArrayList(previousAspects), + new ArrayList(aspects), + M2ModelDiff.TYPE_ASPECT)); + } + else + { + for (AspectDefinition aspect : aspects) + { + M2ModelDiffs.add(new M2ModelDiff(aspect.getName(), + M2ModelDiff.TYPE_ASPECT, + M2ModelDiff.DIFF_CREATED)); + } + } + + if (previousConDefs.size() != 0) + { + M2ModelDiffs + .addAll(M2ConstraintDefinition + .diffConstraintLists( + new ArrayList( + previousConDefs), + new ArrayList( + conDefs))); + } + else + { + for (ConstraintDefinition conDef : conDefs) + { + M2ModelDiffs.add(new M2ModelDiff(conDef.getName(), + M2ModelDiff.TYPE_CONSTRAINT, + M2ModelDiff.DIFF_CREATED)); + } + } + + if (previousImportedNamespaces.size() != 0) + { + M2ModelDiffs + .addAll(M2NamespaceDefinition + .diffNamespaceDefinitionLists( + new ArrayList( + previousImportedNamespaces), + new ArrayList( + importedNamespaces))); + } + else + { + for(NamespaceDefinition namespaceDefinition: importedNamespaces) + { + M2ModelDiffs.add(new M2ModelDiff(namespaceDefinition.getModel().getName(), + namespaceDefinition, + M2ModelDiff.TYPE_NAMESPACE, + M2ModelDiff.DIFF_CREATED)); + } + } + } + } + else + { + if (model != null) + { + // new model + Collection types = model.getTypes(); + Collection aspects = model.getAspects(); + + for (TypeDefinition type : types) + { + M2ModelDiffs.add(new M2ModelDiff(type.getName(), + M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_CREATED)); + } + + for (AspectDefinition aspect : aspects) + { + M2ModelDiffs.add(new M2ModelDiff(aspect.getName(), + M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_CREATED)); + } + } + else + { + // nothing to diff + } + } + + return M2ModelDiffs; + } + + @Override + public ClassLoader getResourceClassLoader() + { + return resourceClassLoader; + } + + @Override + public void setResourceClassLoader(ClassLoader resourceClassLoader) + { + this.resourceClassLoader = resourceClassLoader; + } + + @Override + public String getNamespaceURI(String prefix) + { + return getTenantDictionaryRegistry().getNamespaceURI(prefix); + } + + @Override + public Collection getPrefixes(String URI) + { + return getTenantDictionaryRegistry().getPrefixes(URI); + } + + @Override + public void addURI(String uri) + { + getTenantDictionaryRegistry().addURI(uri); + } + + @Override + public Collection getPrefixes() + { + return Collections.unmodifiableCollection(getTenantDictionaryRegistry() + .getPrefixesCache().keySet()); + } + + @Override + public Collection getURIs() + { + return Collections.unmodifiableCollection(getTenantDictionaryRegistry() + .getUrisCache()); + } + + @Override + public void removeURI(String uri) + { + getTenantDictionaryRegistry().removeURI(uri); + } + + @Override + public void addPrefix(String prefix, String uri) + { + getTenantDictionaryRegistry().addPrefix(prefix, uri); + } + + @Override + public void removePrefix(String prefix) + { + getTenantDictionaryRegistry().removePrefix(prefix); + } + + private AtomicBoolean contextRefreshed = new AtomicBoolean(false); + + @Override + public boolean isContextRefreshed() + { + return contextRefreshed.get(); + } + + @Override + public void onApplicationEvent(ApplicationEvent event) + { + if (event instanceof ContextRefreshedEvent) + { + contextRefreshed.set(true); + } + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/DictionaryListener.java b/src/main/java/org/alfresco/repo/dictionary/DictionaryListener.java new file mode 100644 index 0000000..5c17083 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/DictionaryListener.java @@ -0,0 +1,50 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +/** + * Dictionary Listener interface. + *

+ * This interface allows Dictionary Listeners to register with the DictionaryService. + * + */ +public interface DictionaryListener +{ + /** + * Callback for (re-)initialising the Dictionary caches (executed in the current tenant context) + */ + void onDictionaryInit(); + + /** + * Callback once dictionary destroy is complete (executed in the current tenant context) + */ + void afterDictionaryDestroy(); + + /** + * Callback once dictionary initialisation is complete (executed in the current tenant context) + */ + void afterDictionaryInit(); +} diff --git a/src/main/java/org/alfresco/repo/dictionary/DictionaryNamespaceComponent.java b/src/main/java/org/alfresco/repo/dictionary/DictionaryNamespaceComponent.java new file mode 100644 index 0000000..3bdb0b9 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/DictionaryNamespaceComponent.java @@ -0,0 +1,111 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.Collection; + +import org.alfresco.service.namespace.NamespaceService; + + +/** + * Data Dictionary Namespace Service Implementation + * + * @author David Caruana + */ +public class DictionaryNamespaceComponent implements NamespaceService +{ + /** + * Namespace DAO + */ + private NamespaceDAO namespaceDAO; + + + /** + * Sets the Namespace DAO + * + * @param namespaceDAO namespace DAO + */ + public void setNamespaceDAO(NamespaceDAO namespaceDAO) + { + this.namespaceDAO = namespaceDAO; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.NamespaceService#getURIs() + */ + public Collection getURIs() + { + return namespaceDAO.getURIs(); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.NamespaceService#getPrefixes() + */ + public Collection getPrefixes() + { + return namespaceDAO.getPrefixes(); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.ref.NamespacePrefixResolver#getNamespaceURI(java.lang.String) + */ + public String getNamespaceURI(String prefix) + { + return namespaceDAO.getNamespaceURI(prefix); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.ref.NamespacePrefixResolver#getPrefixes(java.lang.String) + */ + public Collection getPrefixes(String namespaceURI) + { + return namespaceDAO.getPrefixes(namespaceURI); + } + + + /* (non-Javadoc) + * @see org.alfresco.service.namespace.NamespaceService#registerNamespace(java.lang.String, java.lang.String) + */ + public void registerNamespace(String prefix, String uri) + { + // TODO: + throw new UnsupportedOperationException(); + } + + + /* (non-Javadoc) + * @see org.alfresco.service.namespace.NamespaceService#registerNamespace(java.lang.String, java.lang.String) + */ + public void unregisterNamespace(String prefix) + { + // TODO: + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/DictionaryRegistry.java b/src/main/java/org/alfresco/repo/dictionary/DictionaryRegistry.java new file mode 100644 index 0000000..b8686dc --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/DictionaryRegistry.java @@ -0,0 +1,82 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.QName; + +/** + * + * @author sglover + * + */ +public interface DictionaryRegistry +{ + void init(); + void remove(); + CompiledModel getModel(QName name); + boolean modelExists(QName name); + Map> getUriToModels(); + Map getCompiledModels(boolean includeInherited); + QName putModel(CompiledModel model); + void removeModel(QName modelName); + String getTenantDomain(); + void clear(); + List getModelsForUri(String uri); + AspectDefinition getAspect(QName aspectName); + AssociationDefinition getAssociation(QName assocName); + ClassDefinition getClass(QName className); + PropertyDefinition getProperty(QName propertyName); + TypeDefinition getType(QName typeName); + ConstraintDefinition getConstraint(QName constraintQName); + DataTypeDefinition getDataType(QName typeName); + @SuppressWarnings("rawtypes") + DataTypeDefinition getDataType(Class javaClass); + boolean isModelInherited(QName modelName); + Map getPrefixesCache(); + List getUrisCache(); + Collection getPrefixes(String URI); + void addURI(String uri); + boolean hasURI(String uri); + void addPrefix(String prefix, String uri); + boolean hasPrefix(String prefix); + void removeURI(String uri); + void removePrefix(String prefix); + Collection getTypes(boolean includeInherited); + Collection getAssociations(boolean includeInherited); + Collection getAspects(boolean includeInherited); + String getNamespaceURI(String prefix); +} diff --git a/src/main/java/org/alfresco/repo/dictionary/ExtendedDictionaryListener.java b/src/main/java/org/alfresco/repo/dictionary/ExtendedDictionaryListener.java new file mode 100644 index 0000000..a7752ef --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/ExtendedDictionaryListener.java @@ -0,0 +1,39 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +/** + * A dictionary listener that allows listeners to listen for models being added to + * the dictionary. Used by the OpenCMIS dictionary to refresh its registry when new models + * are added to the core dictionary. + * + * @author sglover + * + */ +public interface ExtendedDictionaryListener extends DictionaryListener +{ + void modelAdded(CompiledModel model, String tenant); +} diff --git a/src/main/java/org/alfresco/repo/dictionary/Facetable.java b/src/main/java/org/alfresco/repo/dictionary/Facetable.java new file mode 100644 index 0000000..98fe238 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/Facetable.java @@ -0,0 +1,67 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +/** + * How faceting is to be supported + * + * @author Andy + * + */ +public enum Facetable +{ + /** + * TRUE - faceting is required and enhanced support for this is provided if possible + */ + TRUE, + /** + * UNSET - facet support is unset, standard support is assumed + */ + UNSET, + /** + * FALSE - feceting is not required and will not be supported + */ + FALSE; + + public static String serializer(Facetable facetable) { + return facetable.toString(); + } + + public static Facetable deserializer(String value) { + if (value == null) { + return null; + } else if (value.equalsIgnoreCase(TRUE.toString())) { + return TRUE; + } else if (value.equalsIgnoreCase(FALSE.toString())) { + return FALSE; + } else if (value.equalsIgnoreCase(UNSET.toString())) { + return UNSET; + } else { + throw new IllegalArgumentException( + "Invalid facetable enum value: " + value); + } + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/IndexTokenisationMode.java b/src/main/java/org/alfresco/repo/dictionary/IndexTokenisationMode.java new file mode 100644 index 0000000..1b53a53 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/IndexTokenisationMode.java @@ -0,0 +1,70 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +/** + * How tokenisation is supported in the index. + * + * + * @author andyh + * + */ +public enum IndexTokenisationMode { + /** + * Tokenise the property. If the analyser supported ordering then the field + * supports ordering FTS is supported via analysis. + */ + TRUE, + /** + * Do not tokenise the property. The field supports ordering and pattern + * matching. + */ + FALSE, + /** + * There may be two indexes - one to support ordering and one to support + * search. + */ + BOTH; + + public static String serializer(IndexTokenisationMode indexTokenisationMode) { + return indexTokenisationMode.toString(); + } + + public static IndexTokenisationMode deserializer(String value) { + if (value == null) { + return null; + } else if (value.equalsIgnoreCase(TRUE.toString())) { + return TRUE; + } else if (value.equalsIgnoreCase(FALSE.toString())) { + return FALSE; + } else if (value.equalsIgnoreCase(BOTH.toString())) { + return BOTH; + } else { + throw new IllegalArgumentException( + "Invalid IndexTokenisationMode: " + value); + } + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2AnonymousTypeDefinition.java b/src/main/java/org/alfresco/repo/dictionary/M2AnonymousTypeDefinition.java new file mode 100644 index 0000000..3a315ea --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2AnonymousTypeDefinition.java @@ -0,0 +1,255 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.i18n.StaticMessageLookup; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + + +/** + * Compiled anonymous type definition. + * + * @author David Caruana + * + */ +/*package*/ class M2AnonymousTypeDefinition implements TypeDefinition +{ + private TypeDefinition type; + private Map properties = new HashMap(); + private Map associations = new HashMap(); + private Map childassociations = new HashMap(); + private transient MessageLookup staticMessageLookup = new StaticMessageLookup(); + + /** + * Construct + * + * @param type the primary type + * @param aspects the aspects to combine with the type + */ + /*package*/ M2AnonymousTypeDefinition(TypeDefinition type, Collection aspects) + { + this.type = type; + + // Combine features of type and aspects + properties.putAll(type.getProperties()); + associations.putAll(type.getAssociations()); + childassociations.putAll(type.getChildAssociations()); + for (AspectDefinition aspect : aspects) + { + properties.putAll(aspect.getProperties()); + associations.putAll(aspect.getAssociations()); + childassociations.putAll(aspect.getChildAssociations()); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getModel() + */ + public ModelDefinition getModel() + { + return type.getModel(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.TypeDefinition#getDefaultAspects() + */ + public List getDefaultAspects() + { + return type.getDefaultAspects(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getDefaultAspects(boolean) + */ + public List getDefaultAspects(boolean inherited) + { + return type.getDefaultAspects(inherited); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getDefaultAspectNames() + */ + public Set getDefaultAspectNames() + { + return type.getDefaultAspectNames(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#getName() + */ + public QName getName() + { + return QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "anonymous#" + type.getName().getLocalName()); + } + + @Override + public String getDescription() + { + return getDescription(staticMessageLookup); + } + + @Override + public String getTitle() + { + return getTitle(staticMessageLookup); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#getTitle() + */ + public String getTitle(MessageLookup messageLookup) + { + return type.getTitle(messageLookup); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#getDescription() + */ + public String getDescription(MessageLookup messageLookup) + { + return type.getDescription(messageLookup); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#getParentName() + */ + public QName getParentName() + { + return type.getParentName(); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#isAspect() + */ + public boolean isAspect() + { + return type.isAspect(); + } + + public Boolean getArchive() + { + return type.getArchive(); + } + + public Boolean getIncludedInSuperTypeQuery() + { + return type.getIncludedInSuperTypeQuery(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#getProperties() + */ + public Map getProperties() + { + return Collections.unmodifiableMap(properties); + } + + /** + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getDefaultValues() + */ + public Map getDefaultValues() + { + Map result = new HashMap(5); + + for(Map.Entry entry : properties.entrySet()) + { + PropertyDefinition propertyDefinition = entry.getValue(); + String defaultValue = propertyDefinition.getDefaultValue(); + if (defaultValue != null) + { + result.put(entry.getKey(), defaultValue); + } + } + + return Collections.unmodifiableMap(result); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#getAssociations() + */ + public Map getAssociations() + { + return Collections.unmodifiableMap(associations); + } + + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#isContainer() + */ + public boolean isContainer() + { + return !childassociations.isEmpty(); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#getChildAssociations() + */ + public Map getChildAssociations() + { + return Collections.unmodifiableMap(childassociations); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getAnalyserResourceBundleName() + */ + @Override + public String getAnalyserResourceBundleName() + { + return type.getAnalyserResourceBundleName(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getParentClassDefinition() + */ + @Override + public ClassDefinition getParentClassDefinition() + { + return type.getParentClassDefinition(); + } + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2Aspect.java b/src/main/java/org/alfresco/repo/dictionary/M2Aspect.java new file mode 100644 index 0000000..9442dea --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2Aspect.java @@ -0,0 +1,41 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +/** + * Aspect definition. + * + * @author David Caruana + */ +public class M2Aspect extends M2Class +{ + + /*package*/ M2Aspect() + { + super(); + } + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2AspectDefinition.java b/src/main/java/org/alfresco/repo/dictionary/M2AspectDefinition.java new file mode 100644 index 0000000..834f92b --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2AspectDefinition.java @@ -0,0 +1,80 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.Map; + +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; + + +/** + * Compiled Aspect Definition. + * + * @author David Caruana + */ +/*package*/ class M2AspectDefinition extends M2ClassDefinition + implements AspectDefinition +{ + + /*package*/ M2AspectDefinition(ModelDefinition model, M2Aspect m2Aspect, NamespacePrefixResolver resolver, Map modelProperties, Map modelAssociations) + { + super(model, m2Aspect, resolver, modelProperties, modelAssociations); + } + + @Override + public String getDescription(MessageLookup messageLookup) + { + String value = M2Label.getLabel(model, messageLookup, "aspect", name, "description"); + + // if we don't have a description call the super class + if (value == null) + { + value = super.getDescription(messageLookup); + } + + return value; + } + + @Override + public String getTitle(MessageLookup messageLookup) + { + String value = M2Label.getLabel(model, messageLookup, "aspect", name, "title"); + + // if we don't have a title call the super class + if (value == null) + { + value = super.getTitle(messageLookup); + } + + return value; + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2Association.java b/src/main/java/org/alfresco/repo/dictionary/M2Association.java new file mode 100644 index 0000000..ce70e64 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2Association.java @@ -0,0 +1,46 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + + +/** + * Association definition. + * + * @author David Caruana + */ +public class M2Association extends M2ClassAssociation +{ + + /*package*/ M2Association() + { + } + + /*package*/ M2Association(String name) + { + super(name); + } + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2AssociationDefinition.java b/src/main/java/org/alfresco/repo/dictionary/M2AssociationDefinition.java new file mode 100644 index 0000000..167791a --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2AssociationDefinition.java @@ -0,0 +1,356 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.alfresco.repo.i18n.StaticMessageLookup; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.EqualsHelper; + + +/** + * Compiled Association Definition. + * + * @author David Caruana + */ +/*package*/ class M2AssociationDefinition implements AssociationDefinition +{ + + private ClassDefinition classDef; + private M2ClassAssociation assoc; + private QName name; + private QName targetClassName; + private ClassDefinition targetClass; + private QName sourceRoleName; + private QName targetRoleName; + private transient MessageLookup staticMessageLookup = new StaticMessageLookup(); + + + /** + * Construct + * + * @param classDef ClassDefinition + * @param assoc M2ClassAssociation + * @param resolver NamespacePrefixResolver + */ + /*package*/ M2AssociationDefinition(ClassDefinition classDef, M2ClassAssociation assoc, NamespacePrefixResolver resolver) + { + this.classDef = classDef; + this.assoc = assoc; + + // Resolve names + this.name = QName.createQName(assoc.getName(), resolver); + this.targetClassName = QName.createQName(assoc.getTargetClassName(), resolver); + this.sourceRoleName = QName.createQName(assoc.getSourceRoleName(), resolver); + this.targetRoleName = QName.createQName(assoc.getTargetRoleName(), resolver); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(56); + sb.append("Association") + .append("[ class=").append(classDef) + .append(", name=").append(name) + .append(", target class=").append(targetClassName) + .append(", source role=").append(sourceRoleName) + .append(", target role=").append(targetRoleName) + .append("]"); + return sb.toString(); + } + + + /*package*/ M2ClassAssociation getM2Association() + { + return assoc; + } + + + /*package*/ void resolveDependencies(ModelQuery query) + { + if (targetClassName == null) + { + throw new DictionaryException("d_dictionary.association.target_class_not_specified", name.toPrefixString()); + } + targetClass = query.getClass(targetClassName); + if (targetClass == null) + { + throw new DictionaryException("d_dictionary.association.target_class_not_found", targetClassName.toPrefixString(), name.toPrefixString()); + } + } + + + @Override + public ModelDefinition getModel() + { + return classDef.getModel(); + } + + + @Override + public QName getName() + { + return name; + } + + + @Override + public boolean isChild() + { + return (assoc instanceof M2ChildAssociation); + } + + @Override + public String getTitle() + { + return getTitle(staticMessageLookup); + } + + @Override + public String getDescription() + { + return getDescription(staticMessageLookup); + } + + @Override + public String getTitle(MessageLookup messageLookup) + { + String value = M2Label.getLabel(classDef.getModel(), messageLookup, "association", name, "title"); + if (value == null) + { + value = assoc.getTitle(); + } + return value; + } + + + @Override + public String getDescription(MessageLookup messageLookup) + { + String value = M2Label.getLabel(classDef.getModel(), messageLookup, "association", name, "description"); + if (value == null) + { + value = assoc.getDescription(); + } + return value; + } + + + @Override + public boolean isProtected() + { + return assoc.isProtected(); + } + + + @Override + public ClassDefinition getSourceClass() + { + return classDef; + } + + + @Override + public QName getSourceRoleName() + { + return sourceRoleName; + } + + + @Override + public boolean isSourceMandatory() + { + return assoc.isSourceMandatory(); + } + + + @Override + public boolean isSourceMany() + { + return assoc.isSourceMany(); + } + + + @Override + public ClassDefinition getTargetClass() + { + return targetClass; + } + + + @Override + public QName getTargetRoleName() + { + return targetRoleName; + } + + + @Override + public boolean isTargetMandatory() + { + return assoc.isTargetMandatory(); + } + + + @Override + public boolean isTargetMandatoryEnforced() + { + return assoc.isTargetMandatoryEnforced(); + } + + + @Override + public boolean isTargetMany() + { + return assoc.isTargetMany(); + } + + /* package */ M2ModelDiff diffAssoc(AssociationDefinition assocDef) + { + M2ModelDiff modelDiff = null; + boolean isUpdated = false; + boolean isUpdatedIncrementally = false; + + if (this == assocDef) + { + modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_ASSOCIATION, M2ModelDiff.DIFF_UNCHANGED); + return modelDiff; + } + + // check name - cannot be null + if (! name.equals(assocDef.getName())) + { + isUpdated = true; + } + + // check title + if (! EqualsHelper.nullSafeEquals(getTitle(null), assocDef.getTitle(null), false)) + { + isUpdatedIncrementally = true; + } + + // check description + if (! EqualsHelper.nullSafeEquals(getDescription(null), assocDef.getDescription(null), false)) + { + isUpdatedIncrementally = true; + } + + // check source class qname + if (! EqualsHelper.nullSafeEquals(getSourceClass().getName(), assocDef.getSourceClass().getName())) + { + isUpdated = true; + } + + // check source role qname + if (! EqualsHelper.nullSafeEquals(getSourceRoleName(), assocDef.getSourceRoleName())) + { + isUpdated = true; + } + + // check target class qname + if (! EqualsHelper.nullSafeEquals(getTargetClass().getName(), assocDef.getTargetClass().getName())) + { + isUpdated = true; + } + + // check target role qname + if (! EqualsHelper.nullSafeEquals(getTargetRoleName(), assocDef.getTargetRoleName())) + { + isUpdated = true; + } + + // TODO - additional checks - is... (x7) + + if (isUpdated) + { + modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_ASSOCIATION, M2ModelDiff.DIFF_UPDATED); + } + else if (isUpdatedIncrementally) + { + modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_ASSOCIATION, M2ModelDiff.DIFF_UPDATED_INC); + } + else + { + modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_ASSOCIATION, M2ModelDiff.DIFF_UNCHANGED); + } + + return modelDiff; + } + + /*package*/ static Collection diffAssocLists(Collection previousAssocs, Collection newAssocs) + { + List modelDiffs = new ArrayList(); + + for (AssociationDefinition previousAssoc : previousAssocs) + { + boolean found = false; + for (AssociationDefinition newAssoc : newAssocs) + { + if (newAssoc.getName().equals(previousAssoc.getName())) + { + modelDiffs.add(((M2AssociationDefinition)previousAssoc).diffAssoc(newAssoc)); + found = true; + break; + } + } + + if (! found) + { + modelDiffs.add(new M2ModelDiff(previousAssoc.getName(), M2ModelDiff.TYPE_ASSOCIATION, M2ModelDiff.DIFF_DELETED)); + } + } + + for (AssociationDefinition newAssoc : newAssocs) + { + boolean found = false; + for (AssociationDefinition previousAssoc : previousAssocs) + { + if (newAssoc.getName().equals(previousAssoc.getName())) + { + found = true; + break; + } + } + + if (! found) + { + modelDiffs.add(new M2ModelDiff(newAssoc.getName(), M2ModelDiff.TYPE_ASSOCIATION, M2ModelDiff.DIFF_CREATED)); + } + } + + return modelDiffs; + } + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2ChildAssociation.java b/src/main/java/org/alfresco/repo/dictionary/M2ChildAssociation.java new file mode 100644 index 0000000..fce0f42 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2ChildAssociation.java @@ -0,0 +1,85 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + + +/** + * Child Association definition. + * + * @author David Caruana + * + */ +public class M2ChildAssociation extends M2ClassAssociation +{ + private String requiredChildName = null; + private Boolean allowDuplicateChildName = null; + private Boolean propagateTimestamps = null; + + + /*package*/ M2ChildAssociation() + { + } + + + /*package*/ M2ChildAssociation(String name) + { + super(name); + } + + + public String getRequiredChildName() + { + return requiredChildName; + } + + + public void setRequiredChildName(String requiredChildName) + { + this.requiredChildName = requiredChildName; + } + + + public boolean allowDuplicateChildName() + { + return allowDuplicateChildName == null ? true : allowDuplicateChildName; + } + + + public void setAllowDuplicateChildName(boolean allowDuplicateChildName) + { + this.allowDuplicateChildName = allowDuplicateChildName; + } + + public boolean isPropagateTimestamps() + { + return propagateTimestamps == null ? false : propagateTimestamps; + } + + public void setPropagateTimestamps(boolean propagateTimestamps) + { + this.propagateTimestamps = propagateTimestamps; + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2ChildAssociationDefinition.java b/src/main/java/org/alfresco/repo/dictionary/M2ChildAssociationDefinition.java new file mode 100644 index 0000000..d8c2fcd --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2ChildAssociationDefinition.java @@ -0,0 +1,70 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.namespace.NamespacePrefixResolver; + + +/** + * Compiled Association Definition. + * + * @author David Caruana + */ +/*package*/ class M2ChildAssociationDefinition extends M2AssociationDefinition + implements ChildAssociationDefinition +{ + + /** + * Construct + * @param classDef class definition + * @param assoc child assocation + * @param resolver namespace resolver + */ + /*package*/ M2ChildAssociationDefinition(ClassDefinition classDef, M2ChildAssociation assoc, NamespacePrefixResolver resolver) + { + super(classDef, assoc, resolver); + } + + + public String getRequiredChildName() + { + return ((M2ChildAssociation)getM2Association()).getRequiredChildName(); + } + + + public boolean getDuplicateChildNamesAllowed() + { + return ((M2ChildAssociation)getM2Association()).allowDuplicateChildName(); + } + + + public boolean getPropagateTimestamps() + { + return ((M2ChildAssociation)getM2Association()).isPropagateTimestamps(); + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2Class.java b/src/main/java/org/alfresco/repo/dictionary/M2Class.java new file mode 100644 index 0000000..8bf8738 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2Class.java @@ -0,0 +1,304 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import java.util.regex.Pattern; + +/** + * Abstract Class Definition. + * + * @author David Caruana + * + */ +public abstract class M2Class +{ + public static final Pattern PROPERTY_PLACEHOLDER = Pattern.compile("\\$\\{.*\\}\\|.*"); + + private String name = null; + private String title = null; + private String description = null; + private String parentName = null; + private Boolean archive = null; + private Boolean includedInSuperTypeQuery = null; + private String analyserResourceBundleName = null; + + private List properties = new ArrayList(); + private List propertyOverrides = new ArrayList(); + private List associations = new ArrayList(); + private List mandatoryAspects = new ArrayList(); + + /*package*/ M2Class() + { + } + + public boolean isAspect() + { + return this instanceof M2Aspect; + } + + + public String getName() + { + return name; + } + + + public void setName(String name) + { + this.name = name; + } + + + public String getTitle() + { + return title; + } + + + public void setTitle(String title) + { + this.title = title; + } + + + public String getDescription() + { + return description; + } + + + public void setDescription(String description) + { + this.description = description; + } + + + public String getParentName() + { + return parentName; + } + + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + + public Boolean getArchive() + { + return archive; + } + + public void setArchive(boolean archive) + { + this.archive = Boolean.valueOf(archive); + } + + public Boolean getIncludedInSuperTypeQuery() + { + return includedInSuperTypeQuery; + } + + public void setIncludedInSuperTypeQuery(boolean includedInSuperTypeQuery) + { + this.includedInSuperTypeQuery = Boolean.valueOf(includedInSuperTypeQuery); + } + + public M2Property createProperty(String name) + { + M2Property property = new M2Property(); + property.setName(name); + properties.add(property); + return property; + } + + + public void removeProperty(String name) + { + M2Property property = getProperty(name); + if (property != null) + { + properties.remove(property); + } + } + + + public List getProperties() + { + return Collections.unmodifiableList(properties); + } + + + public M2Property getProperty(String name) + { + for (M2Property candidate : properties) + { + if (candidate.getName().equals(name)) + { + return candidate; + } + } + return null; + } + + + public M2Association createAssociation(String name) + { + M2Association association = new M2Association(); + association.setName(name); + associations.add(association); + return association; + } + + + public M2ChildAssociation createChildAssociation(String name) + { + M2ChildAssociation association = new M2ChildAssociation(); + association.setName(name); + associations.add(association); + return association; + } + + + public void removeAssociation(String name) + { + M2ClassAssociation association = getAssociation(name); + if (association != null) + { + associations.remove(association); + } + } + + + public List getAssociations() + { + return Collections.unmodifiableList(associations); + } + + + public M2ClassAssociation getAssociation(String name) + { + for (M2ClassAssociation candidate : associations) + { + if (candidate.getName().equals(name)) + { + return candidate; + } + } + return null; + } + + + public M2PropertyOverride createPropertyOverride(String name) + { + M2PropertyOverride property = new M2PropertyOverride(); + property.setName(name); + propertyOverrides.add(property); + return property; + } + + + public void removePropertyOverride(String name) + { + M2PropertyOverride property = getPropertyOverride(name); + if (property != null) + { + propertyOverrides.remove(property); + } + } + + + public List getPropertyOverrides() + { + return Collections.unmodifiableList(propertyOverrides); + } + + + public M2PropertyOverride getPropertyOverride(String name) + { + for (M2PropertyOverride candidate : propertyOverrides) + { + if (candidate.getName().equals(name)) + { + return candidate; + } + } + return null; + } + + public void addMandatoryAspect(String name) + { + mandatoryAspects.add(name); + } + + + public void removeMandatoryAspect(String name) + { + mandatoryAspects.remove(name); + } + + + public List getMandatoryAspects() + { + return Collections.unmodifiableList(mandatoryAspects); + } + + /** + * @return String + */ + public String getAnalyserResourceBundleName() + { + return analyserResourceBundleName; + } + + public void setAnalyserResourceBundleName(String analyserResourceBundleName) + { + this.analyserResourceBundleName = analyserResourceBundleName; + } + + public void setConfigProperties(Properties configProperties) + { + if (properties != null) + { + for (M2Property property : properties) + { + property.setConfigProperties(configProperties); + } + } + if (propertyOverrides != null) + { + for (M2PropertyOverride propertyOverride : propertyOverrides) + { + propertyOverride.setConfigProperties(configProperties); + } + } + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2ClassAssociation.java b/src/main/java/org/alfresco/repo/dictionary/M2ClassAssociation.java new file mode 100644 index 0000000..a2c469f --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2ClassAssociation.java @@ -0,0 +1,219 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + + +/** + * Abstract Association Definition. + * + * @author David Caruana + * + */ +public abstract class M2ClassAssociation +{ + private String name = null; + private Boolean isProtected = null; + private String title = null; + private String description = null; + private String sourceRoleName = null; + private Boolean isSourceMandatory = null; + private Boolean isSourceMany = null; + private String targetClassName = null; + private String targetRoleName = null; + private Boolean isTargetMandatory = null; + private Boolean isTargetMandatoryEnforced = null; + private Boolean isTargetMany = null; + + + /*package*/ M2ClassAssociation() + { + } + + + /*package*/ M2ClassAssociation(String name) + { + this.name = name; + } + + + public boolean isChild() + { + return this instanceof M2ChildAssociation; + } + + + public String getName() + { + return name; + } + + + public void setName(String name) + { + this.name = name; + } + + + public boolean isProtected() + { + return isProtected == null ? false : isProtected; + } + + + public void setProtected(boolean isProtected) + { + this.isProtected = isProtected; + } + + + public String getTitle() + { + return title; + } + + + public void setTitle(String title) + { + this.title = title; + } + + + public String getDescription() + { + return description; + } + + + public void setDescription(String description) + { + this.description = description; + } + + + public String getSourceRoleName() + { + return sourceRoleName; + } + + + public void setSourceRoleName(String name) + { + this.sourceRoleName = name; + } + + + public boolean isSourceMandatory() + { + return isSourceMandatory == null ? true : isSourceMandatory; + } + + + public void setSourceMandatory(boolean isSourceMandatory) + { + this.isSourceMandatory = isSourceMandatory; + } + + + public boolean isSourceMany() + { + return isSourceMany == null ? false : isSourceMany; + } + + + public void setSourceMany(boolean isSourceMany) + { + this.isSourceMany = isSourceMany; + } + + + public String getTargetClassName() + { + return targetClassName; + } + + + public void setTargetClassName(String targetClassName) + { + this.targetClassName = targetClassName; + } + + + public String getTargetRoleName() + { + return targetRoleName; + } + + + public void setTargetRoleName(String name) + { + this.targetRoleName = name; + } + + public Boolean getTargetMandatory() + { + return isTargetMandatory(); + } + + public boolean isTargetMandatory() + { + return isTargetMandatory == null ? false : isTargetMandatory; + } + + + public void setTargetMandatory(boolean isTargetMandatory) + { + this.isTargetMandatory = isTargetMandatory; + } + + public Boolean getTargetMandatoryEnforced() + { + return isTargetMandatoryEnforced(); + } + + public boolean isTargetMandatoryEnforced() + { + return isTargetMandatoryEnforced == null ? isTargetMandatory() : isTargetMandatoryEnforced; + } + + + public void setTargetMandatoryEnforced(boolean isTargetMandatoryEnforced) + { + this.isTargetMandatoryEnforced = isTargetMandatoryEnforced; + } + + + public boolean isTargetMany() + { + return isTargetMany == null ? true : isTargetMany; + } + + + public void setTargetMany(boolean isTargetMany) + { + this.isTargetMany = isTargetMany; + } + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2ClassDefinition.java b/src/main/java/org/alfresco/repo/dictionary/M2ClassDefinition.java new file mode 100644 index 0000000..afca702 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2ClassDefinition.java @@ -0,0 +1,781 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import static org.alfresco.service.cmr.dictionary.DictionaryException.DuplicateDefinitionException; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.i18n.StaticMessageLookup; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.EqualsHelper; + + +/** + * Compiled Class Definition + * + * @author David Caruana + */ +/*package*/ class M2ClassDefinition implements ClassDefinition +{ + private static final String ERR_CLASS_NOT_DEFINED_NAMESPACE = "d_dictionary.class_definition.class.namespace_not_defined"; + private static final String ERR_PROPERTY_NOT_DEFINED_NAMESPACE = "d_dictionary.class_definition.property.namespace_not_defined"; + private static final String ERR_DUPLICATE_PROPERTY_DEFINITION = "d_dictionary.class_definition.duplicate.property_definition"; + private static final String ERR_DUPLICATE_PROPERTY_EXISTING_DEF = "d_dictionary.class_definition.duplicate.property_existing_def"; + private static final String ERR_ASSOCIATION_NOT_DEFINED_NAMESPACE = "d_dictionary.class_definition.association.namespace_not_defined"; + private static final String ERR_DUPLICATE_ASSOCIATION_DEFINITION = "d_dictionary.class_definition.duplicate.association_definition"; + private static final String ERR_DUPLICATE_ASSOCIATION_EXISTING_DEF = "d_dictionary.class_definition.duplicate.association_existing_def"; + private static final String ERR_DUPLICATE_PROPERTY_AND_PROPERTY_OVERRIDE = "d_dictionary.class_definition.duplicate.property_and_property_override"; + private static final String ERR_DUPLICATE_PROPERTY_OVERRIDE = "d_dictionary.class_definition.duplicate.property_override_definition"; + private static final String ERR_PARENT_NOT_FOUND = "d_dictionary.class_definition.class.parent_not_found"; + private static final String ERR_PROPERTY_NOT_EXIST = "d_dictionary.class_definition.property_not_exist"; + private static final String ERR_MANDATORY_ASPECT_NOT_FOUND = "d_dictionary.class_definition.mandatory_aspect_not_found"; + private static final String ERR_DUPLICATE_PROPERTY_IN_CLASS_HIERARCHY = "d_dictionary.class_definition.duplicate.property_in_class_hierarchy"; + private static final String ERR_DUPLICATE_ASSOCIATION_IN_CLASS_HIERARCHY = "d_dictionary.class_definition.duplicate.association_in_class_hierarchy"; + + protected ModelDefinition model; + protected M2Class m2Class; + protected QName name; + protected QName parentName = null; + protected ClassDefinition parentClassDefinition; + + private Map propertyOverrides = new HashMap(); + private Map properties = new HashMap(); + private Map inheritedProperties = new HashMap(); + private Map associations = new HashMap(); + private Map inheritedAssociations = new HashMap(); + private Map inheritedChildAssociations = new HashMap(); + private List defaultAspects = new ArrayList(); + private List defaultAspectNames = new ArrayList(); + private List inheritedDefaultAspects = new ArrayList(); + private Set inheritedDefaultAspectNames = new HashSet(); + private Boolean archive = null; + private Boolean inheritedArchive = null; + private Boolean includedInSuperTypeQuery = null; + private Boolean inheritedIncludedInSuperTypeQuery = null; + private String analyserResourceBundleName; + private transient MessageLookup staticMessageLookup = new StaticMessageLookup(); + + /** + * Construct + * + * @param m2Class class definition + * @param resolver namepsace resolver + * @param modelProperties global list of model properties + * @param modelAssociations global list of model associations + */ + /*package*/ M2ClassDefinition(ModelDefinition model, M2Class m2Class, NamespacePrefixResolver resolver, Map modelProperties, Map modelAssociations) + { + this.model = model; + this.m2Class = m2Class; + + // Resolve Names + this.name = QName.createQName(m2Class.getName(), resolver); + if (!model.isNamespaceDefined(name.getNamespaceURI())) + { + throw new DictionaryException(ERR_CLASS_NOT_DEFINED_NAMESPACE, name.toPrefixString(), name.getNamespaceURI(), model.getName().toPrefixString()); + } + this.archive = m2Class.getArchive(); + this.includedInSuperTypeQuery = m2Class.getIncludedInSuperTypeQuery(); + if (m2Class.getParentName() != null && m2Class.getParentName().length() > 0) + { + this.parentName = QName.createQName(m2Class.getParentName(), resolver); + } + + // Construct Properties + for (M2Property property : m2Class.getProperties()) + { + PropertyDefinition def = new M2PropertyDefinition(this, property, resolver); + if (!model.isNamespaceDefined(def.getName().getNamespaceURI()) && + !model.isNamespaceImported(def.getName().getNamespaceURI())) + { + throw new DictionaryException(ERR_PROPERTY_NOT_DEFINED_NAMESPACE, def.getName().toPrefixString(), def.getName().getNamespaceURI(), model.getName().toPrefixString()); + } + if (properties.containsKey(def.getName())) + { + throw new DuplicateDefinitionException(ERR_DUPLICATE_PROPERTY_DEFINITION, def.getName().toPrefixString(), name.toPrefixString()); + } + + // Check for existence of property elsewhere within the model + PropertyDefinition existingDef = modelProperties.get(def.getName()); + if (existingDef != null) + { + // TODO: Consider sharing property, if property definitions are equal + throw new DuplicateDefinitionException(ERR_DUPLICATE_PROPERTY_EXISTING_DEF, def.getName().toPrefixString(), name.toPrefixString(), existingDef.getContainerClass().getName().toPrefixString()); + } + + properties.put(def.getName(), def); + modelProperties.put(def.getName(), def); + } + + // Construct Associations + for (M2ClassAssociation assoc : m2Class.getAssociations()) + { + AssociationDefinition def; + if (assoc instanceof M2ChildAssociation) + { + def = new M2ChildAssociationDefinition(this, (M2ChildAssociation)assoc, resolver); + } + else + { + def = new M2AssociationDefinition(this, assoc, resolver); + } + if (!model.isNamespaceDefined(def.getName().getNamespaceURI())) + { + throw new DictionaryException(ERR_ASSOCIATION_NOT_DEFINED_NAMESPACE, def.getName().toPrefixString(), def.getName().getNamespaceURI(), model.getName().toPrefixString()); + } + if (associations.containsKey(def.getName())) + { + throw new DuplicateDefinitionException(ERR_DUPLICATE_ASSOCIATION_DEFINITION, def.getName().toPrefixString(), name.toPrefixString()); + } + + // Check for existence of association elsewhere within the model + AssociationDefinition existingDef = modelAssociations.get(def.getName()); + if (existingDef != null) + { + // TODO: Consider sharing association, if association definitions are equal + throw new DuplicateDefinitionException(ERR_DUPLICATE_ASSOCIATION_EXISTING_DEF, def.getName().toPrefixString(), name.toPrefixString(), existingDef.getSourceClass().getName().toPrefixString()); + } + + associations.put(def.getName(), def); + modelAssociations.put(def.getName(), def); + } + + // Construct Property overrides + for (M2PropertyOverride override : m2Class.getPropertyOverrides()) + { + QName overrideName = QName.createQName(override.getName(), resolver); + if (properties.containsKey(overrideName)) + { + throw new DuplicateDefinitionException(ERR_DUPLICATE_PROPERTY_AND_PROPERTY_OVERRIDE, overrideName.toPrefixString(), name.toPrefixString()); + } + if (propertyOverrides.containsKey(overrideName)) + { + throw new DuplicateDefinitionException(ERR_DUPLICATE_PROPERTY_OVERRIDE, overrideName.toPrefixString(), name.toPrefixString()); + } + propertyOverrides.put(overrideName, override); + } + + // Resolve qualified names + for (String aspectName : m2Class.getMandatoryAspects()) + { + QName name = QName.createQName(aspectName, resolver); + if (!defaultAspectNames.contains(name)) + { + defaultAspectNames.add(name); + } + } + + this.analyserResourceBundleName = m2Class.getAnalyserResourceBundleName(); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(120); + sb.append("ClassDef") + .append("[name=").append(name) + .append("]"); + return sb.toString(); + } + + + /*package*/ void resolveDependencies( + ModelQuery query, + NamespacePrefixResolver prefixResolver, + Map modelConstraints) + { + if (parentName != null) + { + ClassDefinition parent = query.getClass(parentName); + if (parent == null) + { + throw new DictionaryException(ERR_PARENT_NOT_FOUND, parentName.toPrefixString(), name.toPrefixString()); + } + parentClassDefinition = parent; + } + + for (PropertyDefinition def : properties.values()) + { + ((M2PropertyDefinition)def).resolveDependencies(query, prefixResolver, modelConstraints); + } + for (AssociationDefinition def : associations.values()) + { + ((M2AssociationDefinition)def).resolveDependencies(query); + } + + for (Map.Entry override : propertyOverrides.entrySet()) + { + PropertyDefinition propDef = query.getProperty(override.getKey()); + if (propDef == null) + { + throw new DictionaryException(ERR_PROPERTY_NOT_EXIST, name.toPrefixString(), override.getKey().toPrefixString()); + } + } + + for (QName aspectName : defaultAspectNames) + { + AspectDefinition aspect = query.getAspect(aspectName); + if (aspect == null) + { + throw new DictionaryException(ERR_MANDATORY_ASPECT_NOT_FOUND, aspectName.toPrefixString(), name.toPrefixString()); + } + defaultAspects.add(aspect); + } + } + + + /*package*/ void resolveInheritance( + ModelQuery query, + NamespacePrefixResolver prefixResolver, + Map modelConstraints) + { + // Retrieve parent class + ClassDefinition parentClass = (parentName == null) ? null : query.getClass(parentName); + + // Build list of inherited properties (and process overridden values) + if (parentClass != null) + { + for (PropertyDefinition def : parentClass.getProperties().values()) + { + M2PropertyOverride override = propertyOverrides.get(def.getName()); + if (override == null) + { + inheritedProperties.put(def.getName(), def); + } + else + { + inheritedProperties.put( + def.getName(), + new M2PropertyDefinition(this, def, override, prefixResolver, modelConstraints)); + } + } + } + + // Append list of defined properties + for (PropertyDefinition def : properties.values()) + { + if (inheritedProperties.containsKey(def.getName())) + { + throw new DuplicateDefinitionException(ERR_DUPLICATE_PROPERTY_IN_CLASS_HIERARCHY, def.getName().toPrefixString(), name.toPrefixString()); + } + inheritedProperties.put(def.getName(), def); + } + + // Build list of inherited associations + if (parentClass != null) + { + inheritedAssociations.putAll(parentClass.getAssociations()); + } + + // Append list of defined associations + for (AssociationDefinition def : associations.values()) + { + if (inheritedAssociations.containsKey(def.getName())) + { + throw new DuplicateDefinitionException(ERR_DUPLICATE_ASSOCIATION_IN_CLASS_HIERARCHY, def.getName().toPrefixString(), name.toPrefixString()); + } + inheritedAssociations.put(def.getName(), def); + } + + // Derive Child Associations + for (AssociationDefinition def : inheritedAssociations.values()) + { + if (def instanceof ChildAssociationDefinition) + { + inheritedChildAssociations.put(def.getName(), (ChildAssociationDefinition)def); + } + } + + // Build list of inherited default aspects + if (parentClass != null) + { + inheritedDefaultAspects.addAll(parentClass.getDefaultAspects()); + } + + // Append list of defined default aspects + for (AspectDefinition def : defaultAspects) + { + if (!inheritedDefaultAspects.contains(def)) + { + inheritedDefaultAspects.add(def); + } + } + + // Convert to set of names + for (AspectDefinition aspDef : inheritedDefaultAspects) + { + inheritedDefaultAspectNames.add(aspDef.getName()); + } + + // resolve archive inheritance + if (parentClass != null && archive == null) + { + // archive not explicitly set on this class and there is a parent class + inheritedArchive = ((M2ClassDefinition)parentClass).getArchive(); + } + + // resolve includedInSuperTypeQuery inheritance + if (parentClass != null && includedInSuperTypeQuery == null) + { + // archive not explicitly set on this class and there is a parent class + inheritedIncludedInSuperTypeQuery = ((M2ClassDefinition)parentClass).getIncludedInSuperTypeQuery(); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getModel() + */ + public ModelDefinition getModel() + { + return model; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#getName() + */ + public QName getName() + { + return name; + } + + @Override + public String getDescription() + { + return getDescription(staticMessageLookup); + } + + @Override + public String getTitle() + { + return getTitle(staticMessageLookup); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#getTitle() + */ + public String getTitle(MessageLookup messageLookup) + { + String value = M2Label.getLabel(model, messageLookup, "class", name, "title"); + if (value == null) + { + value = m2Class.getTitle(); + } + return value; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#getDescription() + */ + public String getDescription(MessageLookup messageLookup) + { + String value = M2Label.getLabel(model, messageLookup, "class", name, "description"); + if (value == null) + { + value = m2Class.getDescription(); + } + return value; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#getParentName() + */ + public QName getParentName() + { + return parentName; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#isAspect() + */ + public boolean isAspect() + { + return (m2Class instanceof M2Aspect); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getArchive() + */ + public Boolean getArchive() + { + return archive == null ? inheritedArchive : archive; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#includedInSuperTypeQuery() + */ + public Boolean getIncludedInSuperTypeQuery() + { + if(includedInSuperTypeQuery != null) + { + return includedInSuperTypeQuery; + } + if(inheritedIncludedInSuperTypeQuery != null) + { + return inheritedIncludedInSuperTypeQuery; + } + return Boolean.TRUE; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#getProperties() + */ + public Map getProperties() + { + return Collections.unmodifiableMap(inheritedProperties); + } + + /** + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getDefaultValues() + */ + public Map getDefaultValues() + { + Map result = new HashMap(5); + + for(Map.Entry entry : inheritedProperties.entrySet()) + { + PropertyDefinition propertyDefinition = entry.getValue(); + String defaultValue = propertyDefinition.getDefaultValue(); + if (defaultValue != null) + { + result.put(entry.getKey(), defaultValue); + } + } + + return Collections.unmodifiableMap(result); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#getAssociations() + */ + public Map getAssociations() + { + return Collections.unmodifiableMap(inheritedAssociations); + } + + /** + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getDefaultAspects() + */ + public List getDefaultAspects() + { + return inheritedDefaultAspects; + } + + /** + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getDefaultAspects(boolean) + */ + public List getDefaultAspects(boolean inherited) + { + return inherited ? getDefaultAspects() : defaultAspects; + } + + /** + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getDefaultAspectNames() + */ + public Set getDefaultAspectNames() + { + return inheritedDefaultAspectNames; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#isContainer() + */ + public boolean isContainer() + { + return !inheritedChildAssociations.isEmpty(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ClassDefinition#getChildAssociations() + */ + public Map getChildAssociations() + { + return Collections.unmodifiableMap(inheritedChildAssociations); + } + + @Override + public int hashCode() + { + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (!(obj instanceof M2ClassDefinition)) + { + return false; + } + return name.equals(((M2ClassDefinition)obj).name); + } + + /** + * return differences in class definition + * + * note: + * - checks properties for incremental updates, but does not include the diffs + * - checks assocs & child assocs for incremental updates, but does not include the diffs + * - incremental updates include changes in title/description, property default value, etc + */ + /* package */ List diffClass(ClassDefinition classDef) + { + List modelDiffs = new ArrayList(); + boolean isUpdated = false; + boolean isUpdatedIncrementally = false; + + if (this == classDef) + { + return modelDiffs; + } + + // check name - cannot be null + if (! getName().equals(classDef.getName())) + { + isUpdated = true; + } + + // check title + if (! EqualsHelper.nullSafeEquals(getTitle(null), classDef.getTitle(null), false)) + { + isUpdatedIncrementally = true; + } + + // check description + if (! EqualsHelper.nullSafeEquals(getDescription(null), classDef.getDescription(null), false)) + { + isUpdatedIncrementally = true; + } + + // check parent name + if (getParentName() != null) + { + if (! getParentName().equals(classDef.getParentName())) + { + isUpdated = true; + } + } + else if (classDef.getParentName() != null) + { + isUpdated = true; + } + + // check if aspect (or type) + if (isAspect() != classDef.isAspect()) + { + isUpdated = true; + } + + // check if container + if (isContainer() != classDef.isContainer()) + { + if (isContainer()) + { + // updated (non-incrementally) if class was a container and now is not a container - ie. all child associations removed + isUpdated = true; + } + + if (classDef.isContainer()) + { + // updated incrementally if class was not a container and now is a container - ie. some child associations added + isUpdatedIncrementally = true; + } + } + + // check all properties (including inherited properties) + Collection propertyDiffs = M2PropertyDefinition.diffPropertyLists(getProperties().values(), classDef.getProperties().values()); + + modelDiffs.addAll(propertyDiffs); + + // check all associations (including inherited associations, child associations and inherited child associations) + Collection assocDiffs = M2AssociationDefinition.diffAssocLists(getAssociations().values(), classDef.getAssociations().values()); + + modelDiffs.addAll(assocDiffs); + + // check default/mandatory aspects + for (AspectDefinition newAspect : classDef.getDefaultAspects(false)) + { + boolean found = false; + for (AspectDefinition previousAspect : getDefaultAspects(false)) + { + if (newAspect.getName().equals(previousAspect.getName())) + { + found = true; + break; + } + } + + if (! found) + { + // mandatory aspect added (to aspect or type) + isUpdated = true; + } + } + + // check archive/inheritedArchive + if (getArchive() == null) + { + if (classDef.getArchive() != null) + { + isUpdatedIncrementally = true; + } + } + else + { + Boolean classArchive = classDef.getArchive(); + if (classArchive == null || classArchive.booleanValue() != getArchive().booleanValue()) + { + isUpdatedIncrementally = true; + } + } + + // check includedInSuperTypeQuery/inheritedIncludedInSuperTypeQuery + if (getIncludedInSuperTypeQuery() == null) + { + // belts-and-braces (currently does not return null) + if (classDef.getIncludedInSuperTypeQuery() != null) + { + isUpdatedIncrementally = true; + } + } + else + { + Boolean classIncludedInSuperTypeQuery = classDef.getIncludedInSuperTypeQuery(); + if (classIncludedInSuperTypeQuery == null || classIncludedInSuperTypeQuery.booleanValue() != getIncludedInSuperTypeQuery().booleanValue()) + { + isUpdatedIncrementally = true; + } + } + + String modelDiffType; + if (isAspect()) + { + modelDiffType = M2ModelDiff.TYPE_ASPECT; + } + else + { + modelDiffType = M2ModelDiff.TYPE_TYPE; + } + + if (isUpdated) + { + modelDiffs.add(new M2ModelDiff(name, modelDiffType, M2ModelDiff.DIFF_UPDATED)); + } + else if (isUpdatedIncrementally) + { + modelDiffs.add(new M2ModelDiff(name, modelDiffType, M2ModelDiff.DIFF_UPDATED_INC)); + } + else + { + modelDiffs.add(new M2ModelDiff(name, modelDiffType, M2ModelDiff.DIFF_UNCHANGED)); + } + + return modelDiffs; + } + + /** + * return differences in class definition lists + * + */ + /*package*/ static List diffClassLists(Collection previousClasses, Collection newClasses, String M2ModelDiffType) + { + List modelDiffs = new ArrayList(); + + for (ClassDefinition previousClass : previousClasses) + { + boolean found = false; + for (ClassDefinition newClass : newClasses) + { + if (newClass.getName().equals(previousClass.getName())) + { + modelDiffs.addAll(((M2ClassDefinition)previousClass).diffClass(newClass)); + found = true; + break; + } + } + + if (! found) + { + modelDiffs.add(new M2ModelDiff(previousClass.getName(), M2ModelDiffType, M2ModelDiff.DIFF_DELETED)); + } + } + + for (ClassDefinition newClass : newClasses) + { + boolean found = false; + for (ClassDefinition previousClass : previousClasses) + { + if (newClass.getName().equals(previousClass.getName())) + { + found = true; + break; + } + } + + if (! found) + { + modelDiffs.add(new M2ModelDiff(newClass.getName(), M2ModelDiffType, M2ModelDiff.DIFF_CREATED)); + } + } + + return modelDiffs; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getAnalyserResourceBundleName() + */ + @Override + public String getAnalyserResourceBundleName() + { + return analyserResourceBundleName; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getParentClassDefinition() + */ + @Override + public ClassDefinition getParentClassDefinition() + { + return parentClassDefinition; + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2Constraint.java b/src/main/java/org/alfresco/repo/dictionary/M2Constraint.java new file mode 100644 index 0000000..11f8082 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2Constraint.java @@ -0,0 +1,139 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.ArrayList; +import java.util.List; + +/** + * Abstract Property Constraint. + * + * @author Derek Hulley + */ +public class M2Constraint +{ + private String name; + private String ref; + private String type; + private String title; + private String description; + private List parameters = new ArrayList(2); + + /*package*/ M2Constraint() + { + } + + @Override + public String toString() + { + return this.name; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getRef() + { + return ref; + } + + public void setRef(String refName) + { + this.ref = refName; + } + + public String getType() + { + return type; + } + + public void setType(String type) + { + this.type = type; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public String getDescription() + { + return description; + } + + public void setDescription(String description) + { + this.description = description; + } + + public List getParameters() + { + return parameters; + } + + public M2NamedValue createParameter(String name, String simpleValue) + { + M2NamedValue param = new M2NamedValue(); + param.setName(name); + param.setSimpleValue(simpleValue); + parameters.add(param); + return param; + } + + public M2NamedValue createParameter(String name, List listValue) + { + M2NamedValue param = new M2NamedValue(); + param.setName(name); + param.setListValue(listValue); + parameters.add(param); + return param; + } + + public void removeParameter(String name) + { + List params = new ArrayList(getParameters()); + for (M2NamedValue param : params) + { + if (param.getName().equals(name)) + { + parameters.remove(param); + } + } + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2ConstraintDefinition.java b/src/main/java/org/alfresco/repo/dictionary/M2ConstraintDefinition.java new file mode 100644 index 0000000..10da726 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2ConstraintDefinition.java @@ -0,0 +1,523 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.alfresco.repo.dictionary.constraint.ListOfValuesConstraint; +import org.alfresco.repo.dictionary.constraint.NumericRangeConstraint; +import org.alfresco.repo.dictionary.constraint.RegexConstraint; +import org.alfresco.repo.dictionary.constraint.RegisteredConstraint; +import org.alfresco.repo.dictionary.constraint.StringLengthConstraint; +import org.alfresco.repo.i18n.StaticMessageLookup; +import org.alfresco.service.cmr.dictionary.Constraint; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.EqualsHelper; +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.BeanWrapperImpl; +import org.springframework.beans.InvalidPropertyException; +import org.springframework.beans.PropertyAccessException; + +/** + * Compiled Property Constraint + * + * @author Derek Hulley. janv + */ +/* package */class M2ConstraintDefinition implements ConstraintDefinition +{ + private static final String PROP_SHORT_NAME = "shortName"; + private static final String PROP_TITLE = "title"; + private static final String PROP_DESCRIPTION = "description"; + + public static final String ERR_CYCLIC_REF = "d_dictionary.constraint.err.cyclic_ref"; + public static final String ERR_TYPE_AND_REF = "d_dictionary.constraint.err.type_and_ref"; + public static final String ERR_TYPE_OR_REF = "d_dictionary.constraint.err.type_or_ref"; + public static final String ERR_REF_NOT_FOUND = "d_dictionary.constraint.err.ref_not_found"; + public static final String ERR_ANON_NEEDS_PROPERTY = "d_dictionary.constraint.err.anon_needs_property"; + public static final String ERR_INVALID_TYPE = "d_dictionary.constraint.err.invalid_type"; + public static final String ERR_SIMPLE_AND_LIST = "d_dictionary.constraint.err.property_simple_and_list"; + public static final String ERR_CONSTRUCT_FAILURE = "d_dictionary.constraint.err.construct_failure"; + public static final String ERR_PROPERTY_MISMATCH = "d_dictionary.constraint.err.property_mismatch"; + public static final String ERR_RESERVED_PROPERTY = "d_dictionary.constraint.err.reserved_property"; + public static final String ERR_NAMESPACE_NOT_DEFINED = "d_dictionary.constraint.err.namespace_not_defined"; + + private static int anonPropCount = 0; + + private ModelDefinition model; + private NamespacePrefixResolver prefixResolver; + private M2Constraint m2Constraint; + private QName name; + private Constraint constraint; + private boolean resolving; + private transient MessageLookup staticMessageLookup = new StaticMessageLookup(); + + /* package */M2ConstraintDefinition(M2PropertyDefinition m2PropertyDef, M2Constraint m2Constraint, + NamespacePrefixResolver prefixResolver) + { + this(m2PropertyDef.getModel(), m2PropertyDef, m2Constraint, prefixResolver); + } + + /* package */M2ConstraintDefinition(ModelDefinition modelDefinition, M2PropertyDefinition m2PropertyDef, + M2Constraint m2Constraint, NamespacePrefixResolver prefixResolver) + { + this.model = modelDefinition; + this.m2Constraint = m2Constraint; + this.prefixResolver = prefixResolver; + + String constraintName = m2Constraint.getName(); + if (constraintName == null) + { + // the constraint is anonymous, so it has to be defined within the context of a property + if (m2PropertyDef == null) + { + throw new DictionaryException(ERR_ANON_NEEDS_PROPERTY); + } + // pick the name up from the property and some anonymous value + String localName = m2PropertyDef.getName().getLocalName() + "_anon_" + (++anonPropCount); + this.name = QName.createQName(m2PropertyDef.getName().getNamespaceURI(), localName); + m2Constraint.setName(this.name.getPrefixedQName(prefixResolver).toPrefixString()); + } + else + { + this.name = QName.createQName(m2Constraint.getName(), prefixResolver); + if (!model.isNamespaceDefined(name.getNamespaceURI())) + { + throw new DictionaryException(ERR_NAMESPACE_NOT_DEFINED, name.toPrefixString(), name.getNamespaceURI(), model.getName().toPrefixString()); + } + } + } + + /* package */synchronized void resolveDependencies(ModelQuery query, boolean enableConstraintClassLoading) + { + if (resolving) + { + throw new DictionaryException(ERR_CYCLIC_REF, name.toPrefixString()); + } + // prevent circular references + try + { + resolving = true; + resolveInternal(query, enableConstraintClassLoading); + } + finally + { + resolving = false; + } + } + + private synchronized void resolveInternal(ModelQuery query, boolean enableConstraintClassLoading) + { + if (constraint != null) + { + // already been resolved + return; + } + + String shortName = name.toPrefixString(); + String ref = m2Constraint.getRef(); + String type = m2Constraint.getType(); + + String title = m2Constraint.getTitle(); + String description = m2Constraint.getDescription(); + + if (ref != null && type != null) + { + throw new DictionaryException(ERR_TYPE_AND_REF, shortName); + } + else if (ref == null && type == null) + { + throw new DictionaryException(ERR_TYPE_OR_REF, shortName); + } + else if (ref != null) + { + // resolve the reference name + QName qnameRef = QName.createQName(ref, prefixResolver); + // ensure that the reference exists in the model + M2ConstraintDefinition constraintDef = (M2ConstraintDefinition) query.getConstraint(qnameRef); + if (constraintDef == null) + { + throw new DictionaryException(ERR_REF_NOT_FOUND, ref, shortName); + } + // make sure that the constraint definition has itself been resolved + constraintDef.resolveDependencies(query, enableConstraintClassLoading); + // just use the constraint provided by the referenced definition + this.constraint = constraintDef.getConstraint(); + + //use real constraint name instead of anonymous name + //TODO Fix backed out - breaks DictionaryDAOTest + //this.name = constraintDef.getName(); + + + if (m2Constraint.getTitle() == null) + { + m2Constraint.setTitle(constraintDef.getTitle(null)); + } + + if (m2Constraint.getDescription() == null) + { + m2Constraint.setDescription(constraintDef.getDescription(null)); + } + } + else + { + // we have to build the constraint from the type + try + { + ConstraintType constraintType = ConstraintType.valueOf(type); + constraint = constraintType.newInstance(); + } + catch (IllegalArgumentException e) + { + // try to establish it as a class + try + { + if(enableConstraintClassLoading) + { + @SuppressWarnings("unchecked") + Class clazz = Class.forName(type); + constraint = (Constraint) clazz.newInstance(); + } + } + catch (ClassNotFoundException ee) + { + throw new DictionaryException(ERR_INVALID_TYPE, type, shortName); + } + catch (ClassCastException ee) + { + throw new DictionaryException(ERR_INVALID_TYPE, type, shortName); + } + catch (Exception ee) + { + throw new DictionaryException(ERR_CONSTRUCT_FAILURE, type, shortName); + } + } + + if(constraint != null) + { + // property setters + BeanWrapper beanWrapper = new BeanWrapperImpl(constraint); + List constraintNamedValues = m2Constraint.getParameters(); + + if (constraintNamedValues != null) + { + for (M2NamedValue namedValue : constraintNamedValues) + { + String namedValueName = namedValue.getName(); + // Check for reserved properties + if (namedValueName.equals(PROP_SHORT_NAME)) + { + throw new DictionaryException(ERR_RESERVED_PROPERTY, PROP_SHORT_NAME, namedValueName); + } + + Object value = null; + if (namedValue.getSimpleValue() != null && namedValue.getListValue() != null) + { + throw new DictionaryException(ERR_SIMPLE_AND_LIST, shortName, namedValue.getName()); + } + else if (namedValue.getSimpleValue() != null) + { + value = namedValue.getSimpleValue(); + } + else if (namedValue.getListValue() != null) + { + value = namedValue.getListValue(); + } + try + { + beanWrapper.setPropertyValue(namedValueName, value); + } + catch (PropertyAccessException e) + { + throw new DictionaryException(ERR_PROPERTY_MISMATCH, e, namedValueName, shortName); + } + catch (InvalidPropertyException e) + { + throw new DictionaryException(ERR_PROPERTY_MISMATCH, e, namedValueName, shortName); + } + } + + // Pass in the short name as a special property, if it is available + if (beanWrapper.isWritableProperty(PROP_SHORT_NAME)) + { + try + { + beanWrapper.setPropertyValue(PROP_SHORT_NAME, shortName); + } + catch (PropertyAccessException e) + { + throw new DictionaryException(ERR_PROPERTY_MISMATCH, e, shortName, shortName); + } + catch (InvalidPropertyException e) + { + throw new DictionaryException(ERR_PROPERTY_MISMATCH, e, shortName, shortName); + } + } + + if ((title != null) && (beanWrapper.isWritableProperty(PROP_TITLE))) + { + beanWrapper.setPropertyValue(PROP_TITLE, title); + } + + if ((title != null) && (beanWrapper.isWritableProperty(PROP_DESCRIPTION))) + { + beanWrapper.setPropertyValue(PROP_DESCRIPTION, description); + } + } + + // now initialize + constraint.initialize(); + } + } + } + + /** + * @see #getName() + */ + @Override + public String toString() + { + return getName().toString(); + } + + @Override + public ModelDefinition getModel() + { + return model; + } + + @Override + public QName getName() + { + return name; + } + + @Override + public String getTitle() + { + return getTitle(staticMessageLookup); + } + + @Override + public String getDescription() + { + return getDescription(staticMessageLookup); + } + + @Override + public String getTitle(MessageLookup messageLookup) + { + String value = M2Label.getLabel(model, messageLookup, "constraint", name, "title"); + if (value == null) + { + value = m2Constraint.getTitle(); + } + return value; + } + + @Override + public String getDescription(MessageLookup messageLookup) + { + String value = M2Label.getLabel(model, messageLookup, "constraint", name, "description"); + if (value == null) + { + value = m2Constraint.getDescription(); + } + return value; + } + + @Override + public Constraint getConstraint() + { + return constraint; + } + + @Override + public QName getRef() + { + QName refQName = null; + String ref = m2Constraint.getRef(); + if (ref != null) + { + refQName = QName.createQName(ref, prefixResolver); + } + return refQName; + } + + /** + * Well-known constraint types + */ + public static enum ConstraintType + { + REGISTERED + { + @Override + protected Constraint newInstance() + { + return new RegisteredConstraint(); + } + }, + REGEX + { + @Override + protected Constraint newInstance() + { + return new RegexConstraint(); + } + }, + MINMAX + { + @Override + protected Constraint newInstance() + { + return new NumericRangeConstraint(); + } + }, + LENGTH + { + @Override + protected Constraint newInstance() + { + return new StringLengthConstraint(); + } + }, + LIST + { + @Override + protected Constraint newInstance() + { + return new ListOfValuesConstraint(); + } + }; + + /** + * @return Returns the constraint implementation + */ + protected abstract Constraint newInstance(); + } + + /* package */ M2ModelDiff diffConstraint(ConstraintDefinition conDef) + { + M2ModelDiff modelDiff = null; + boolean isUpdated = false; + boolean isUpdatedIncrementally = false; + + if (this == conDef) + { + modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_CONSTRAINT, M2ModelDiff.DIFF_UNCHANGED); + return modelDiff; + } + + // check name - cannot be null + if (! name.equals(conDef.getName())) + { + isUpdated = true; + } + + // check title + if (! EqualsHelper.nullSafeEquals(getTitle(null), conDef.getTitle(null), false)) + { + isUpdatedIncrementally = true; + } + + // check description + if (! EqualsHelper.nullSafeEquals(getDescription(null), conDef.getDescription(null), false)) + { + isUpdatedIncrementally = true; + } + + // check type string + if (! EqualsHelper.nullSafeEquals(getConstraint().getType(), conDef.getConstraint().getType())) + { + isUpdated = true; + } + + if (isUpdated) + { + modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_CONSTRAINT, M2ModelDiff.DIFF_UPDATED); + } + else if (isUpdatedIncrementally) + { + modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_CONSTRAINT, M2ModelDiff.DIFF_UPDATED_INC); + } + else + { + modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_CONSTRAINT, M2ModelDiff.DIFF_UNCHANGED); + } + + return modelDiff; + } + + /*package*/ static Collection diffConstraintLists(Collection previousConstraints, Collection newConstraints) + { + List modelDiffs = new ArrayList(); + + for (ConstraintDefinition previousConstraint : previousConstraints) + { + boolean found = false; + for (ConstraintDefinition newConstraint : newConstraints) + { + if (newConstraint.getName().equals(previousConstraint.getName())) + { + modelDiffs.add(((M2ConstraintDefinition)previousConstraint).diffConstraint(previousConstraint)); + found = true; + break; + } + } + + if (! found) + { + modelDiffs.add(new M2ModelDiff(previousConstraint.getName(), M2ModelDiff.TYPE_CONSTRAINT, M2ModelDiff.DIFF_DELETED)); + } + } + + for (ConstraintDefinition newConstraint : newConstraints) + { + boolean found = false; + for (ConstraintDefinition previousConstraint : previousConstraints) + { + if (newConstraint.getName().equals(previousConstraint.getName())) + { + found = true; + break; + } + } + + if (! found) + { + modelDiffs.add(new M2ModelDiff(newConstraint.getName(), M2ModelDiff.TYPE_CONSTRAINT, M2ModelDiff.DIFF_CREATED)); + } + } + + return modelDiffs; + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2DataType.java b/src/main/java/org/alfresco/repo/dictionary/M2DataType.java new file mode 100644 index 0000000..4f6104a --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2DataType.java @@ -0,0 +1,125 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + + +/** + * Property Type Definition + * + * @author David Caruana + * + */ +public class M2DataType +{ + private String name = null; + private String title = null; + private String description = null; + private String defaultAnalyserClassName = null; + private String javaClassName = null; + private String analyserResourceBundleName = null; + + + /*package*/ M2DataType() + { + super(); + } + + + public String getName() + { + return name; + } + + + public void setName(String name) + { + this.name = name; + } + + + public String getTitle() + { + return title; + } + + + public void setTitle(String title) + { + this.title = title; + } + + + public String getDescription() + { + return description; + } + + + public void setDescription(String description) + { + this.description = description; + } + + + public String getDefaultAnalyserClassName() + { + return defaultAnalyserClassName; + } + + + public void setDefaultAnalyserClassName(String defaultAnalyserClassName) + { + this.defaultAnalyserClassName = defaultAnalyserClassName;; + } + + + public String getJavaClassName() + { + return javaClassName; + } + + + public void setJavaClassName(String javaClassName) + { + this.javaClassName = javaClassName;; + } + + + /** + * @return String + */ + public String getAnalyserResourceBundleName() + { + return analyserResourceBundleName; + } + + + public void setAnalyserResourceBundleName(String analyserResourceBundleName) + { + this.analyserResourceBundleName = analyserResourceBundleName; + } + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2DataTypeDefinition.java b/src/main/java/org/alfresco/repo/dictionary/M2DataTypeDefinition.java new file mode 100644 index 0000000..bc72b14 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2DataTypeDefinition.java @@ -0,0 +1,253 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.Locale; +import java.util.ResourceBundle; + +import org.springframework.extensions.surf.util.I18NUtil; +import org.springframework.util.StringUtils; +import org.alfresco.repo.i18n.StaticMessageLookup; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; + + +/** + * Compiled Property Type Definition + * + * @author David Caruana + * + */ +/*package*/ class M2DataTypeDefinition implements DataTypeDefinition +{ + private static final String ERR_NOT_DEFINED_NAMESPACE = "d_dictionary.data_type.namespace_not_defined"; + private static final String ERR_JAVA_CLASS_NOT_SPECIFIED = "d_dictionary.data_type.java_class_not_specified"; + private static final String ERR_JAVA_CLASS_INVALID = "d_dictionary.data_type.java_class_invalid"; + + private ModelDefinition model; + private QName name; + private M2DataType dataType; + private String analyserResourceBundleName; + private transient MessageLookup staticMessageLookup = new StaticMessageLookup(); + + + /*package*/ M2DataTypeDefinition(ModelDefinition model, M2DataType propertyType, NamespacePrefixResolver resolver) + { + this.model = model; + this.name = QName.createQName(propertyType.getName(), resolver); + if (!model.isNamespaceDefined(name.getNamespaceURI())) + { + throw new DictionaryException(ERR_NOT_DEFINED_NAMESPACE, name.toPrefixString(), name.getNamespaceURI(), model.getName().toPrefixString()); + } + this.dataType = propertyType; + this.analyserResourceBundleName = dataType.getAnalyserResourceBundleName(); + } + + + /*package*/ void resolveDependencies(ModelQuery query) + { + // Ensure java class has been specified + String javaClass = dataType.getJavaClassName(); + if (javaClass == null) + { + throw new DictionaryException(ERR_JAVA_CLASS_NOT_SPECIFIED, name.toPrefixString()); + } + + // Ensure java class is valid and referenceable + try + { + Class.forName(javaClass); + } + catch (ClassNotFoundException e) + { + throw new DictionaryException(ERR_JAVA_CLASS_INVALID, javaClass, name.toPrefixString(), e); + } + } + + /** + * @see #getName() + */ + @Override + public String toString() + { + return getName().toString(); + } + + @Override + public ModelDefinition getModel() + { + return model; + } + + @Override + public QName getName() + { + return name; + } + + @Override + public String getTitle() + { + return getTitle(staticMessageLookup); + } + + @Override + public String getDescription() + { + return getDescription(staticMessageLookup); + } + + @Override + public String getTitle(MessageLookup messageLookup) + { + String value = M2Label.getLabel(model, messageLookup, "datatype", name, "title"); + if (value == null) + { + value = dataType.getTitle(); + } + return value; + } + + @Override + public String getDescription(MessageLookup messageLookup) + { + String value = M2Label.getLabel(model, messageLookup, "datatype", name, "description"); + if (value == null) + { + value = dataType.getDescription(); + } + return value; + } + + @Override + public String getDefaultAnalyserClassName() + { + return dataType.getDefaultAnalyserClassName(); + } + + @Override + public String getJavaClassName() + { + return dataType.getJavaClassName(); + } + + @Override + public String getAnalyserResourceBundleName() + { + return analyserResourceBundleName; + } + + @Override + public String resolveAnalyserClassName() + { + return resolveAnalyserClassName(I18NUtil.getLocale()); + } + + /** + * @param locale Locale + * @return String + */ + @Override + public String resolveAnalyserClassName(Locale locale) + { + ClassLoader resourceBundleClassLoader = getModel().getDictionaryDAO().getResourceClassLoader(); + if(resourceBundleClassLoader == null) + { + resourceBundleClassLoader = this.getClass().getClassLoader(); + } + + StringBuilder keyBuilder = new StringBuilder(64); + keyBuilder.append(getModel().getName().toPrefixString()); + keyBuilder.append(".datatype"); + keyBuilder.append(".").append(getName().toPrefixString()); + keyBuilder.append(".analyzer"); + String key = StringUtils.replace(keyBuilder.toString(), ":", "_"); + + String analyserClassName = null; + + String defaultAnalyserResourceBundleName = this.getModel().getDictionaryDAO().getDefaultAnalyserResourceBundleName(); + if(defaultAnalyserResourceBundleName != null) + { + ResourceBundle bundle = ResourceBundle.getBundle(defaultAnalyserResourceBundleName, locale, resourceBundleClassLoader); + if(bundle.containsKey(key)) + { + analyserClassName = bundle.getString(key); + } + } + + String analyserResourceBundleName; + if(analyserClassName == null) + { + analyserResourceBundleName = dataType.getAnalyserResourceBundleName(); + if(analyserResourceBundleName != null) + { + ResourceBundle bundle = ResourceBundle.getBundle(analyserResourceBundleName, locale, resourceBundleClassLoader); + if(bundle.containsKey(key)) + { + analyserClassName = bundle.getString(key); + } + } + } + + if(analyserClassName == null) + { + analyserResourceBundleName = getModel().getAnalyserResourceBundleName(); + if(analyserResourceBundleName != null) + { + ResourceBundle bundle = ResourceBundle.getBundle(analyserResourceBundleName, locale, resourceBundleClassLoader); + if(bundle.containsKey(key)) + { + analyserClassName = bundle.getString(key); + } + } + } + + if(analyserClassName == null) + { + // MLTEXT should fall back to TEXT for analysis + if(name.equals(DataTypeDefinition.MLTEXT)) + { + analyserClassName = model.getDictionaryDAO().getDataType(DataTypeDefinition.TEXT).resolveAnalyserClassName(locale); + if(analyserClassName == null) + { + analyserClassName = dataType.getDefaultAnalyserClassName(); + } + } + else + { + analyserClassName = dataType.getDefaultAnalyserClassName(); + } + } + + return analyserClassName; + } + + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2Label.java b/src/main/java/org/alfresco/repo/dictionary/M2Label.java new file mode 100644 index 0000000..e206ea2 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2Label.java @@ -0,0 +1,91 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.Locale; + +import org.springframework.extensions.surf.util.I18NUtil; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.alfresco.service.namespace.QName; +import org.springframework.util.StringUtils; + + +/** + * Helper for obtaining display labels for data dictionary items + * + * @author David Caruana + */ +public class M2Label +{ + + /** + * Get label for data dictionary item given specified locale + * + * @param locale Locale + * @param model ModelDefinition + * @param messageLookup MessageLookup + * @param type String + * @param item QName + * @param label String + * @return String + */ + public static String getLabel(Locale locale, ModelDefinition model, MessageLookup messageLookup, String type, QName item, String label) + { + if (messageLookup == null) + { + return null; + } + String key = model.getName().toPrefixString(); + if (type != null) + { + key += "." + type; + } + if (item != null) + { + key += "." + item.toPrefixString(); + } + key += "." + label; + key = StringUtils.replace(key, ":", "_"); + return messageLookup.getMessage(key, locale); + } + + /** + * Get label for data dictionary item + * + * @param model ModelDefinition + * @param messageLookup MessageLookup + * @param type String + * @param item QName + * @param label String + * @return String + */ + public static String getLabel(ModelDefinition model, MessageLookup messageLookup, String type, QName item, String label) + { + return getLabel(I18NUtil.getLocale(), model, messageLookup, type, item, label); + } + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2Model.java b/src/main/java/org/alfresco/repo/dictionary/M2Model.java new file mode 100644 index 0000000..e11862b --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2Model.java @@ -0,0 +1,505 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Properties; +import java.util.zip.CRC32; + +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.jibx.runtime.BindingDirectory; +import org.jibx.runtime.IBindingFactory; +import org.jibx.runtime.IMarshallingContext; +import org.jibx.runtime.IUnmarshallingContext; +import org.jibx.runtime.JiBXException; + + +/** + * Model Definition. + * + * @author David Caruana + * + */ +public class M2Model +{ + private static final String ERR_PARSE_FAILURE = "d_dictionary.model.err.parse.failure"; + private static final String ERR_CREATE_M2MODEL_FAILURE = "d_dictionary.model.err.create_m2model.failure"; + + private String name = null; + private String description = null; + private String author = null; + private Date published = null; + private String version; + private String analyserResourceBundleName = null; + + private List namespaces = new ArrayList(); + private List imports = new ArrayList(); + private List dataTypes = new ArrayList(); + private List types = new ArrayList(); + private List aspects = new ArrayList(); + private List constraints = new ArrayList(); + + private M2Model() + { + } + + + /** + * Construct an empty model + * + * @param name the name of the model + * @return the model + */ + public static M2Model createModel(String name) + { + M2Model model = new M2Model(); + model.name = name; + return model; + } + + + /** + * Construct a model from a dictionary xml specification + * + * @param xml the dictionary xml + * @return the model representation of the xml + */ + public static M2Model createModel(InputStream xml) + { + return createModel("default", xml); + } + + public static M2Model createModel(String bindingName, InputStream xml) + { + try + { + IBindingFactory factory = BindingDirectory.getFactory(bindingName, M2Model.class); + IUnmarshallingContext context = factory.createUnmarshallingContext(); + Object obj = context.unmarshalDocument(xml, null); + return (M2Model)obj; + } + catch(JiBXException e) + { + throw new DictionaryException(ERR_PARSE_FAILURE, e); + } + } + + + /** + * Render the model to dictionary XML + * + * @param xml the dictionary xml representation of the model + */ + public void toXML(OutputStream xml) + { + toXML(null, xml); + } + + public void toXML(ModelDefinition.XMLBindingType bindingType, OutputStream xml) + { + try + { + if(bindingType == null) + { + bindingType = ModelDefinition.XMLBindingType.DEFAULT; + } + + String bindingName = bindingType.toString(); + IBindingFactory factory = (bindingName != null) ? BindingDirectory.getFactory(bindingName, M2Model.class) : + BindingDirectory.getFactory("default", M2Model.class); + IMarshallingContext context = factory.createMarshallingContext(); + context.setIndent(4); + context.marshalDocument(this, "UTF-8", null, xml); + } + catch(JiBXException e) + { + throw new DictionaryException(ERR_CREATE_M2MODEL_FAILURE, e); + } + } + + public long getChecksum(ModelDefinition.XMLBindingType bindingType) + { + final CRC32 crc = new CRC32(); + + // construct the crc directly from the model's xml stream + toXML(bindingType, new OutputStream() { + public void write(int b) throws IOException + { + crc.update(b); + } + }); + + return crc.getValue(); + } + + /** + * Create a compiled form of this model + * + * @param dictionaryDAO dictionary DAO + * @param namespaceDAO namespace DAO + * @return the compiled form of the model + */ + public CompiledModel compile(DictionaryDAO dictionaryDAO, NamespaceDAO namespaceDAO, boolean enableConstraintClassLoading) + { + CompiledModel compiledModel = new CompiledModel(this, dictionaryDAO, namespaceDAO, enableConstraintClassLoading); + return compiledModel; + } + + + public String getName() + { + return name; + } + + + public void setName(String name) + { + this.name = name; + } + + + public String getDescription() + { + return description; + } + + + public void setDescription(String description) + { + this.description = description; + } + + + public String getAuthor() + { + return author; + } + + + public void setAuthor(String author) + { + this.author = author; + } + + + public Date getPublishedDate() + { + return published; + } + + + public void setPublishedDate(Date published) + { + this.published = published; + } + + + public String getVersion() + { + return version; + } + + + public void setVersion(String version) + { + this.version = version; + } + + + public M2Type createType(String name) + { + M2Type type = new M2Type(); + type.setName(name); + types.add(type); + return type; + } + + + public void removeType(String name) + { + M2Type type = getType(name); + if (type != null) + { + types.remove(type); + } + } + + + public List getTypes() + { + return Collections.unmodifiableList(types); + } + + + public M2Type getType(String name) + { + for (M2Type candidate : types) + { + if (candidate.getName().equals(name)) + { + return candidate; + } + } + return null; + } + + + public M2Aspect createAspect(String name) + { + M2Aspect aspect = new M2Aspect(); + aspect.setName(name); + aspects.add(aspect); + return aspect; + } + + + public void removeAspect(String name) + { + M2Aspect aspect = getAspect(name); + if (aspect != null) + { + aspects.remove(aspect); + } + } + + + public List getAspects() + { + return Collections.unmodifiableList(aspects); + } + + + public M2Aspect getAspect(String name) + { + for (M2Aspect candidate : aspects) + { + if (candidate.getName().equals(name)) + { + return candidate; + } + } + return null; + } + + + public M2DataType createPropertyType(String name) + { + M2DataType type = new M2DataType(); + type.setName(name); + dataTypes.add(type); + return type; + } + + + public void removePropertyType(String name) + { + M2DataType type = getPropertyType(name); + if (type != null) + { + dataTypes.remove(name); + } + } + + + public List getPropertyTypes() + { + return Collections.unmodifiableList(dataTypes); + } + + + public M2DataType getPropertyType(String name) + { + for (M2DataType candidate : dataTypes) + { + if (candidate.getName().equals(name)) + { + return candidate; + } + } + return null; + } + + + public M2Namespace createNamespace(String uri, String prefix) + { + M2Namespace namespace = new M2Namespace(); + namespace.setUri(uri); + namespace.setPrefix(prefix); + namespaces.add(namespace); + return namespace; + } + + + public void removeNamespace(String uri) + { + M2Namespace namespace = getNamespace(uri); + if (namespace != null) + { + namespaces.remove(namespace); + } + } + + + public List getNamespaces() + { + return Collections.unmodifiableList(namespaces); + } + + + public M2Namespace getNamespace(String uri) + { + for (M2Namespace candidate : namespaces) + { + if (candidate.getUri().equals(uri)) + { + return candidate; + } + } + return null; + } + + + public M2Namespace createImport(String uri, String prefix) + { + M2Namespace namespace = new M2Namespace(); + namespace.setUri(uri); + namespace.setPrefix(prefix); + imports.add(namespace); + return namespace; + } + + + public void removeImport(String uri) + { + M2Namespace namespace = getImport(uri); + if (namespace != null) + { + imports.remove(namespace); + } + } + + + public List getImports() + { + return Collections.unmodifiableList(imports); + } + + + public M2Namespace getImport(String uri) + { + for (M2Namespace candidate : imports) + { + if (candidate.getUri().equals(uri)) + { + return candidate; + } + } + return null; + } + + public List getConstraints() + { + return Collections.unmodifiableList(constraints); + } + + public M2Constraint getConstraint(String name) + { + for (M2Constraint candidate : constraints) + { + if (candidate.getName().equals(name)) + { + return candidate; + } + } + return null; + } + + public M2Constraint createConstraint(String name, String type) + { + M2Constraint constraint = new M2Constraint(); + constraint.setName(name); + constraint.setType(type); + constraints.add(constraint); + return constraint; + } + + public void removeConstraint(String name) + { + M2Constraint constraint = getConstraint(name); + if (constraint != null) + { + constraints.remove(constraint); + } + } + + // Do not delete: referenced by m2binding.xml + @SuppressWarnings("unused") + private static List createList() + { + return new ArrayList(); + } + + + /** + * @return String + */ + public String getAnalyserResourceBundleName() + { + return analyserResourceBundleName; + } + + + public void setAnalyserResourceBundleName(String analyserResourceBundleName) + { + this.analyserResourceBundleName = analyserResourceBundleName; + } + + public void setConfigProperties(Properties configProperties) + { + if (types != null) + { + for (M2Class type : types) + { + type.setConfigProperties(configProperties); + } + } + if (aspects != null) + { + for (M2Class aspect : aspects) + { + aspect.setConfigProperties(configProperties); + } + } + } + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2ModelDefinition.java b/src/main/java/org/alfresco/repo/dictionary/M2ModelDefinition.java new file mode 100644 index 0000000..a240743 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2ModelDefinition.java @@ -0,0 +1,196 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.NamespaceDefinition; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; + +/** + * Compiled Model Definition + * + * @author David Caruana + * + */ +public class M2ModelDefinition implements ModelDefinition +{ + private QName name; + private M2Model model; + private String analyserResourceBundleName; + private DictionaryDAO dictionaryDAO; + + + /*package*/ M2ModelDefinition(M2Model model, NamespacePrefixResolver resolver, DictionaryDAO dictionaryDAO) + { + this.name = QName.createQName(model.getName(), resolver); + this.model = model; + this.analyserResourceBundleName = model.getAnalyserResourceBundleName(); + this.dictionaryDAO = dictionaryDAO; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ModelDefinition#getName() + */ + public QName getName() + { + return name; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ModelDefinition#getDescription() + */ + public String getDescription(MessageLookup messageLookup) + { + String value = M2Label.getLabel(this, messageLookup, null, null, "description"); + if (value == null) + { + value = model.getDescription(); + } + return value; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ModelDefinition#getAuthor() + */ + public String getAuthor() + { + return model.getAuthor(); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ModelDefinition#getPublishedDate() + */ + public Date getPublishedDate() + { + return model.getPublishedDate(); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.ModelDefinition#getVersion() + */ + public String getVersion() + { + return model.getVersion(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ModelDefinition#getNamespaces() + */ + public Collection getNamespaces() + { + List namespaces = new ArrayList(); + for (M2Namespace namespace : model.getNamespaces()) + { + namespaces.add(new M2NamespaceDefinition(this, namespace.getUri(), namespace.getPrefix())); + } + return namespaces; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ModelDefinition#isNamespaceDefined(java.lang.String) + */ + public boolean isNamespaceDefined(String uri) + { + for (M2Namespace namespace : model.getNamespaces()) + { + if (namespace.getUri().equals(uri)) + { + return true; + } + } + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ModelDefinition#getImportedNamespaces() + */ + public Collection getImportedNamespaces() + { + List namespaces = new ArrayList(); + for (M2Namespace namespace : model.getImports()) + { + namespaces.add(new M2NamespaceDefinition(this, namespace.getUri(), namespace.getPrefix())); + } + return namespaces; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ModelDefinition#isNamespaceImported(java.lang.String) + */ + public boolean isNamespaceImported(String uri) + { + for (M2Namespace namespace : model.getImports()) + { + if (namespace.getUri().equals(uri)) + { + return true; + } + } + return false; + } + + public void toXML(XMLBindingType bindingType, OutputStream xml) + { + model.toXML(bindingType, xml); + } + + public long getChecksum(XMLBindingType bindingType) + { + return model.getChecksum(bindingType); + } + + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.ModelDefinition#getAnalyserResourceBundleName() + */ + @Override + public String getAnalyserResourceBundleName() + { + return analyserResourceBundleName; + } + + + @Override + public DictionaryDAO getDictionaryDAO() + { + return dictionaryDAO; + } + + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2ModelDiff.java b/src/main/java/org/alfresco/repo/dictionary/M2ModelDiff.java new file mode 100644 index 0000000..d734b34 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2ModelDiff.java @@ -0,0 +1,131 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.dictionary.NamespaceDefinition; +import org.alfresco.service.namespace.QName; +import org.springframework.extensions.surf.util.ParameterCheck; + +/** + * Compiled Model Difference + * + * @author JanV + * + */ +public class M2ModelDiff +{ + public static final String DIFF_CREATED = "created"; + public static final String DIFF_UPDATED = "updated"; + public static final String DIFF_UPDATED_INC = "updated_inc"; // incremental update + public static final String DIFF_DELETED = "deleted"; + public static final String DIFF_UNCHANGED = "unchanged"; + + public static final String TYPE_TYPE = "TYPE"; + public static final String TYPE_ASPECT = "ASPECT"; + public static final String TYPE_DEFAULT_ASPECT = "DEFAULT_ASPECT"; + public static final String TYPE_PROPERTY = "PROPERTY"; + public static final String TYPE_ASSOCIATION = "ASSOCIATION"; + public static final String TYPE_CONSTRAINT = "TYPE_CONSTRAINT"; + public static final String TYPE_NAMESPACE = "TYPE_NAMESPACE"; + + private static final String ERR_UNKNOWN_ELEMENT_TYPE = "d_dictionary.model_diff.element_type.unknown"; + private static final String ERR_UNKNOWN_DIFF_TYPE = "d_dictionary.model_diff.diff_type.unknown"; + + private QName elementName; + private NamespaceDefinition namespace; + private String elementType; + private String diffType; + + public M2ModelDiff(QName elementName, String elementType, String diffType) + { + initModelDiff(elementName, elementType, diffType); + } + + public M2ModelDiff(QName elementName, NamespaceDefinition namespace, String elementType, String diffType) + { + initModelDiff(elementName, elementType, diffType); + this.namespace = namespace; + } + + private void initModelDiff(QName elementName, String elementType, String diffType) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("elementName", elementName); + ParameterCheck.mandatoryString("elementType", elementType); + ParameterCheck.mandatoryString("diffType", diffType); + + if ((!elementType.equals(TYPE_TYPE)) && + (!elementType.equals(TYPE_ASPECT)) && + (!elementType.equals(TYPE_DEFAULT_ASPECT)) && + (!elementType.equals(TYPE_PROPERTY)) && + (!elementType.equals(TYPE_ASSOCIATION)) && + (!elementType.equals(TYPE_CONSTRAINT)) && + (!elementType.equals(TYPE_NAMESPACE)) + ) + { + throw new AlfrescoRuntimeException(ERR_UNKNOWN_ELEMENT_TYPE, new Object[] { elementType }); + } + + if ((! diffType.equals(DIFF_CREATED)) && + (! diffType.equals(DIFF_UPDATED)) && + (! diffType.equals(DIFF_UPDATED_INC)) && + (! diffType.equals(DIFF_DELETED)) && + (! diffType.equals(DIFF_UNCHANGED))) + { + throw new AlfrescoRuntimeException(ERR_UNKNOWN_DIFF_TYPE, new Object[] { diffType }); + } + + this.elementName = elementName; + this.elementType = elementType; + this.diffType = diffType; + } + + public QName getElementName() + { + return elementName; + } + + public String getElementType() + { + return elementType; + } + + public String getDiffType() + { + return diffType; + } + + public NamespaceDefinition getNamespaceDefinition() + { + return namespace; + } + + public String toString() + { + return new String(elementType + " " + elementName + " " + diffType); + } + } \ No newline at end of file diff --git a/src/main/java/org/alfresco/repo/dictionary/M2NamedValue.java b/src/main/java/org/alfresco/repo/dictionary/M2NamedValue.java new file mode 100644 index 0000000..fcd6e62 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2NamedValue.java @@ -0,0 +1,98 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.List; + +/** + * Definition of a named value that can be used for property injection. + * + * @author Derek Hulley + */ +public class M2NamedValue +{ + private String name; + private String simpleValue = null; + private List listValue = null; + + /*package*/ M2NamedValue() + { + } + + + @Override + public String toString() + { + return (name + "=" + (simpleValue == null ? listValue : simpleValue)); + } + + public String getName() + { + return name; + } + + /** + * @return Returns the raw, unconverted value + */ + public String getSimpleValue() + { + return simpleValue; + } + + /** + * @return Returns the list of raw, unconverted values + */ + public List getListValue() + { + return listValue; + } + + public void setName(String name) + { + this.name = name; + } + + public void setSimpleValue(String simpleValue) + { + this.simpleValue = simpleValue; + } + + public void setListValue(List listValue) + { + this.listValue = listValue; + } + + public boolean hasSimpleValue() + { + return (this.simpleValue != null); + } + + public boolean hasListValue() + { + return (this.listValue != null); + } + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2Namespace.java b/src/main/java/org/alfresco/repo/dictionary/M2Namespace.java new file mode 100644 index 0000000..ef1f7f5 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2Namespace.java @@ -0,0 +1,69 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + + +/** + * Namespace Definition. + * + * @author David Caruana + * + */ +public class M2Namespace +{ + private String uri = null; + private String prefix = null; + + + /*package*/ M2Namespace() + { + } + + + public String getUri() + { + return uri; + } + + + public void setUri(String uri) + { + this.uri = uri; + } + + + public String getPrefix() + { + return prefix; + } + + + public void setPrefix(String prefix) + { + this.prefix = prefix; + } + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2NamespaceDefinition.java b/src/main/java/org/alfresco/repo/dictionary/M2NamespaceDefinition.java new file mode 100644 index 0000000..245b6ca --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2NamespaceDefinition.java @@ -0,0 +1,115 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.NamespaceDefinition; + + +/** + * Namespace Definition. + * + * + */ +public class M2NamespaceDefinition implements NamespaceDefinition +{ + ModelDefinition model = null; + private String uri = null; + private String prefix = null; + + + /*package*/ M2NamespaceDefinition(ModelDefinition model, String uri, String prefix) + { + this.model = model; + this.uri = uri; + this.prefix = prefix; + } + + public ModelDefinition getModel() + { + return model; + } + + public String getUri() + { + return uri; + } + + public String getPrefix() + { + return prefix; + } + + static List diffNamespaceDefinitionLists(Collection previousNamespaces, Collection newNamespaces) + { + List modelDiffs = new ArrayList(); + + for (NamespaceDefinition previousNamespace: previousNamespaces) + { + boolean found = false; + for (NamespaceDefinition newNamespace : newNamespaces) + { + if (newNamespace.getUri().equals(previousNamespace.getUri())) + { + if(!newNamespace.getPrefix().equals(previousNamespace.getPrefix())) + { + modelDiffs.add(new M2ModelDiff(newNamespace.getModel().getName(), newNamespace, M2ModelDiff.TYPE_NAMESPACE, M2ModelDiff.DIFF_UPDATED)); + } + found = true; + break; + } + } + + if (!found) + { + modelDiffs.add(new M2ModelDiff(previousNamespace.getModel().getName(), M2ModelDiff.TYPE_NAMESPACE, M2ModelDiff.DIFF_DELETED)); + } + } + + for (NamespaceDefinition newNamespace : newNamespaces) + { + boolean found = false; + for (NamespaceDefinition previousNamespace : previousNamespaces) + { + if (newNamespace.getUri().equals(previousNamespace.getUri())) + { + found = true; + break; + } + } + + if (!found) + { + modelDiffs.add(new M2ModelDiff(newNamespace.getModel().getName(), newNamespace, M2ModelDiff.TYPE_NAMESPACE, M2ModelDiff.DIFF_CREATED)); + } + } + return modelDiffs; + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2Property.java b/src/main/java/org/alfresco/repo/dictionary/M2Property.java new file mode 100644 index 0000000..f00e49f --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2Property.java @@ -0,0 +1,334 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + + +/** + * Property Definition + * + * @author David Caruana + * + */ +public class M2Property +{ + private boolean isOverride = false; + private String name = null; + private String title = null; + private String description = null; + private String propertyType = null; + private boolean isProtected = false; + private boolean isMandatory = false; + private boolean isMandatoryEnforced = false; + private boolean isMultiValued = false; + private String defaultValue = null; + private Boolean isIndexed = Boolean.TRUE; + private Boolean isIndexedAtomically = null; + private Boolean isStoredInIndex = null; + private Boolean isFacetable = null; + private IndexTokenisationMode indexTokenisationMode = null; + private String analyserResourceBundleName; + private List constraints = new ArrayList(); + private Properties configProperties = new Properties(); + + /*package*/ M2Property() + { + } + + + /*package*/ M2Property(String name) + { + this.name = name; + } + + public boolean isOverride() + { + return isOverride; + } + + public void setOverride(boolean isOverride) + { + this.isOverride = isOverride; + } + + public String getName() + { + return name; + } + + + public void setName(String name) + { + this.name = name; + } + + + public String getTitle() + { + return title; + } + + + public void setTitle(String title) + { + this.title = title; + } + + + public String getDescription() + { + return description; + } + + + public void setDescription(String description) + { + this.description = description; + } + + + public String getType() + { + return propertyType; + } + + + public void setType(String type) + { + this.propertyType = type; + } + + + public boolean isProtected() + { + return isProtected; + } + + + public void setProtected(boolean isProtected) + { + this.isProtected = isProtected; + } + + + public boolean isMandatory() + { + return isMandatory; + } + + public void setMandatory(boolean isMandatory) + { + this.isMandatory = isMandatory; + } + + public boolean isMandatoryEnforced() + { + return isMandatoryEnforced; + } + + public void setMandatoryEnforced(boolean isMandatoryEnforced) + { + this.isMandatoryEnforced = isMandatoryEnforced; + } + + public boolean isMultiValued() + { + return isMultiValued; + } + + + public void setMultiValued(boolean isMultiValued) + { + this.isMultiValued = isMultiValued; + } + + + public String getDefaultValue() + { + if (defaultValue != null && M2Class.PROPERTY_PLACEHOLDER.matcher(defaultValue).matches()) + { + String key = defaultValue.substring(defaultValue.indexOf("${") + 2, defaultValue.indexOf("}")); + String value = defaultValue.substring(defaultValue.indexOf("|") + 1); + + return configProperties.getProperty(key, value); + } + else + { + return defaultValue; + } + } + + + public void setDefaultValue(String defaultValue) + { + this.defaultValue = defaultValue; + } + + + public Boolean isIndexed() + { + return isIndexed; + } + + + public void setIndexed(boolean isIndexed) + { + this.isIndexed = Boolean.valueOf(isIndexed); + } + + + public Boolean isStoredInIndex() + { + return isStoredInIndex; + } + + + public void setStoredInIndex(boolean isStoredInIndex) + { + this.isStoredInIndex = Boolean.valueOf(isStoredInIndex); + } + + public Boolean isFacetable() + { + return isFacetable; + } + + + public void setFacetable(boolean isFacetable) + { + this.isFacetable = Boolean.valueOf(isFacetable); + } + + + public Boolean isIndexedAtomically() + { + return isIndexedAtomically; + } + + + public void setIndexedAtomically(boolean isIndexedAtomically) + { + this.isIndexedAtomically = Boolean.valueOf(isIndexedAtomically); + } + + + public IndexTokenisationMode getIndexTokenisationMode() + { + return indexTokenisationMode; + } + + + public void setIndexTokenisationMode(IndexTokenisationMode indexTokenisationMode) + { + this.indexTokenisationMode = indexTokenisationMode; + } + + + public List getConstraints() + { + if (constraints == null) + { + return Collections.emptyList(); + } + else + { + return constraints; + } + } + + public boolean hasConstraints() + { + return ((this.constraints != null) && (constraints.size() > 0)); + } + + public M2Constraint addConstraintRef(String refName) + { + M2Constraint constraint = new M2Constraint(); + constraint.setRef(refName); + constraints.add(constraint); + return constraint; + } + + public M2Constraint addConstraint(String name, String type) + { + M2Constraint constraint = new M2Constraint(); + constraint.setName(name); + constraint.setType(type); + constraints.add(constraint); + return constraint; + } + + public void removeConstraintRef(String refName) + { + List cons = new ArrayList(getConstraints()); + for (M2Constraint con : cons) + { + if (con.getRef().equals(refName)) + { + constraints.remove(con); + } + } + } + + public void removeConstraint(String name) + { + if(name == null) + { + return; + } + List cons = new ArrayList(getConstraints()); + for (M2Constraint con : cons) + { + if (name.equals(con.getName())) + { + constraints.remove(con); + } + } + } + + /** + * @return String + */ + public String getAnalyserResourceBundleName() + { + return analyserResourceBundleName; + } + + + public void setAnalyserResourceBundleName(String analyserResourceBundleName) + { + this.analyserResourceBundleName = analyserResourceBundleName; + } + + public void setConfigProperties(Properties configProperties) + { + this.configProperties = configProperties; + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2PropertyDefinition.java b/src/main/java/org/alfresco/repo/dictionary/M2PropertyDefinition.java new file mode 100644 index 0000000..e69a2c7 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2PropertyDefinition.java @@ -0,0 +1,783 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import static org.alfresco.service.cmr.dictionary.DictionaryException.DuplicateDefinitionException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +import org.alfresco.repo.i18n.StaticMessageLookup; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.EqualsHelper; +import org.springframework.extensions.surf.util.I18NUtil; +import org.springframework.util.StringUtils; + + +/** + * Compiled Property Definition + * + * @author David Caruana + */ +/*package*/ class M2PropertyDefinition implements PropertyDefinition +{ + private ClassDefinition classDef; + private M2Property m2Property; + private QName name; + private QName propertyTypeName; + private DataTypeDefinition dataType; + private String analyserResourceBundleName; + private List constraintDefs = Collections.emptyList(); + private transient MessageLookup staticMessageLookup = new StaticMessageLookup(); + + /*package*/ M2PropertyDefinition( + ClassDefinition classDef, + M2Property m2Property, + NamespacePrefixResolver prefixResolver) + { + this.classDef = classDef; + this.m2Property = m2Property; + + // Resolve Names + this.name = QName.createQName(m2Property.getName(), prefixResolver); + this.propertyTypeName = QName.createQName(m2Property.getType(), prefixResolver); + this.analyserResourceBundleName = m2Property.getAnalyserResourceBundleName(); + } + + + /*package*/ M2PropertyDefinition( + ClassDefinition classDef, + PropertyDefinition propertyDef, + M2PropertyOverride override, + NamespacePrefixResolver prefixResolver, + Map modelConstraints) + { + this.classDef = classDef; + this.name = propertyDef.getName(); + this.dataType = propertyDef.getDataType(); + this.propertyTypeName = this.dataType.getName(); + this.m2Property = createOverriddenProperty(propertyDef, override, prefixResolver, modelConstraints); + } + + + /*package*/ void resolveDependencies( + ModelQuery query, + NamespacePrefixResolver prefixResolver, + Map modelConstraints) + { + if (propertyTypeName == null) + { + throw new DictionaryException( + "d_dictionary.property.err.property_type_not_specified", + name.toPrefixString()); + } + dataType = query.getDataType(propertyTypeName); + if (dataType == null) + { + throw new DictionaryException( + "d_dictionary.property.err.property_type_not_found", + propertyTypeName.toPrefixString(), name.toPrefixString()); + } + + // ensure content properties are not multi-valued + if (propertyTypeName.equals(DataTypeDefinition.CONTENT) && isMultiValued()) + { + throw new DictionaryException("d_dictionary.property.err.single_valued_content"); + } + + // Construct constraints + constraintDefs = buildConstraints( + m2Property.getConstraints(), + this, + prefixResolver, + modelConstraints); + } + + private static List buildConstraints( + List m2constraints, + M2PropertyDefinition m2PropertyDef, + NamespacePrefixResolver prefixResolver, + Map modelConstraints) + { + List constraints = new ArrayList(5); + Map constraintsByQName = new HashMap(7); + for (M2Constraint constraint : m2constraints) + { + // Consistent naming scheme for anonymous property constraints + if (constraint.getName() == null) + { + StringBuilder builder = new StringBuilder(); + builder.append(m2PropertyDef.classDef.getModel().getName().getLocalName()).append("_"); + builder.append(m2PropertyDef.classDef.getName().getLocalName()).append("_"); + builder.append(m2PropertyDef.getName().getLocalName()).append("_"); + builder.append("anon_"); + builder.append(constraints.size()); + QName newName = QName.createQName(m2PropertyDef.classDef.getModel().getName().getNamespaceURI(), builder.toString()); + constraint.setName(newName.getPrefixedQName(prefixResolver).toPrefixString()); + } + + ConstraintDefinition def = new M2ConstraintDefinition(m2PropertyDef, constraint, prefixResolver); + QName qname = def.getName(); + if (constraintsByQName.containsKey(qname)) + { + throw new DuplicateDefinitionException( + "d_dictionary.property.err.duplicate_constraint_on_property", + def.getName().toPrefixString(), m2PropertyDef.name.toPrefixString()); + } + else if (modelConstraints.containsKey(qname)) + { + throw new DuplicateDefinitionException( + "d_dictionary.model.err.duplicate_constraint_on_model", + def.getName().toPrefixString(), def.getModel().getName().toPrefixString()); + } + constraintsByQName.put(qname, def); + constraints.add(def); + modelConstraints.put(qname, def); + } + // done + return constraints; + } + + /** + * Create a property definition whose values are overridden + * + * @param propertyDef the property definition to override + * @param override the overridden values + * @return the property definition + */ + private M2Property createOverriddenProperty( + PropertyDefinition propertyDef, + M2PropertyOverride override, + NamespacePrefixResolver prefixResolver, + Map modelConstraints) + { + M2Property property = new M2Property(); + property.setOverride(true); + + // Process Default Value + String defaultValue = override.getDefaultValue(); + property.setDefaultValue(defaultValue == null ? propertyDef.getDefaultValue() : defaultValue); + + // Process Mandatory Value + Boolean isOverrideMandatory = override.isMandatory(); + Boolean isOverrideMandatoryEnforced = override.isMandatoryEnforced(); + if (isOverrideMandatory != null && propertyDef.isMandatory()) + { + // the override specified whether the property should be mandatory or not + // check that the mandatory enforcement is not relaxed + if (!isOverrideMandatory) + { + throw new DictionaryException( + "d_dictionary.property.err.cannot_relax_mandatory", + propertyDef.getName().toPrefixString()); + } + else if ((isOverrideMandatoryEnforced != null) && !isOverrideMandatoryEnforced && propertyDef.isMandatoryEnforced()) + { + throw new DictionaryException( + "d_dictionary.property.err.cannot_relax_mandatory_enforcement", + propertyDef.getName().toPrefixString()); + } + } + property.setMandatory(isOverrideMandatory == null ? propertyDef.isMandatory() : isOverrideMandatory); + property.setMandatoryEnforced(isOverrideMandatoryEnforced == null ? propertyDef.isMandatoryEnforced() : isOverrideMandatoryEnforced); + + // inherit or override constraints + List overrideConstraints = override.getConstraints(); + if (overrideConstraints != null) + { + constraintDefs = buildConstraints( + overrideConstraints, + this, + prefixResolver, + modelConstraints); + } + else + { + this.constraintDefs = propertyDef.getConstraints(); + } + + // Copy all other properties as they are + property.setDescription(propertyDef.getDescription(null)); + property.setIndexed(propertyDef.isIndexed()); + property.setIndexedAtomically(propertyDef.isIndexedAtomically()); + property.setMultiValued(propertyDef.isMultiValued()); + property.setProtected(propertyDef.isProtected()); + property.setStoredInIndex(propertyDef.isStoredInIndex()); + property.setTitle(propertyDef.getTitle(null)); + property.setIndexTokenisationMode(propertyDef.getIndexTokenisationMode()); + + return property; + } + + /** + * @see #getName() + */ + @Override + public String toString() + { + StringBuffer sb = new StringBuffer(); + sb.append("Name: " + getName() + "\n"); + sb.append("Title: " + getTitle(null) + "\n"); + sb.append("Description: " + getDescription(null) + "\n"); + sb.append("Default Value: " + getDefaultValue() + "\n"); + sb.append("DataType Name: " + getDataType().getName() + "\n"); + sb.append("ContainerClass Name: " + getContainerClass().getName() + "\n"); + sb.append("isMultiValued: " + isMultiValued() + "\n"); + sb.append("isMandatory: " + isMandatory() + "\n"); + sb.append("isMandatoryEnforced: " + isMandatoryEnforced() + "\n"); + sb.append("isProtected: " + isProtected() + "\n"); + sb.append("isIndexed: " + isIndexed() + "\n"); + sb.append("isStoredInIndex: " + isStoredInIndex() + "\n"); + sb.append("isIndexedAtomically: " + isIndexedAtomically() + "\n"); + sb.append("indexTokenisationMode: " + getIndexTokenisationMode() + "\n"); + + return sb.toString(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getModel() + */ + public ModelDefinition getModel() + { + return classDef.getModel(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.PropertyDefinition#getName() + */ + public QName getName() + { + return name; + } + + public String getTitle() + { + return getTitle(staticMessageLookup); + } + + public String getTitle(MessageLookup messageLookup) + { + String value = M2Label.getLabel(classDef.getModel(), messageLookup, "property", name, "title"); + if (value == null) + { + value = m2Property.getTitle(); + } + return value; + } + + public String getTitle(MessageLookup messageLookup, Locale locale) + { + String value = M2Label.getLabel(locale, classDef.getModel(), messageLookup, "property", name, "title"); + if (value == null) + { + value = m2Property.getTitle(); + } + return value; + } + + public String getDescription() + { + return getDescription(staticMessageLookup); + } + + public String getDescription(MessageLookup messageLookup) + { + String value = M2Label.getLabel(classDef.getModel(), messageLookup, "property", name, "description"); + if (value == null) + { + value = m2Property.getDescription(); + } + return value; + } + + public String getDescription(MessageLookup messageLookup, Locale locale) + { + String value = M2Label.getLabel(locale, classDef.getModel(), messageLookup, "property", name, "description"); + if (value == null) + { + value = m2Property.getDescription(); + } + return value; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.PropertyDefinition#getDefaultValue() + */ + public String getDefaultValue() + { + return m2Property.getDefaultValue(); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.PropertyDefinition#getPropertyType() + */ + public DataTypeDefinition getDataType() + { + return dataType; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.PropertyDefinition#getContainerClass() + */ + public ClassDefinition getContainerClass() + { + return classDef; + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isOverride() + */ + public boolean isOverride() + { + return m2Property.isOverride(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.PropertyDefinition#isMultiValued() + */ + public boolean isMultiValued() + { + return m2Property.isMultiValued(); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.PropertyDefinition#isMandatory() + */ + public boolean isMandatory() + { + return m2Property.isMandatory(); + } + + public boolean isMandatoryEnforced() + { + return m2Property.isMandatoryEnforced(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.PropertyDefinition#isProtected() + */ + public boolean isProtected() + { + return m2Property.isProtected(); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.PropertyDefinition#isIndexed() + */ + public boolean isIndexed() + { + if(m2Property.isIndexed() == null) + { + return true; + } + else + { + return m2Property.isIndexed(); + } + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.PropertyDefinition#isStoredInIndex() + */ + public boolean isStoredInIndex() + { + if(m2Property.isStoredInIndex() == null) + { + return false; + } + else + { + return m2Property.isStoredInIndex(); + } + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.PropertyDefinition#isIndexedAtomically() + */ + public boolean isIndexedAtomically() + { + if(m2Property.isIndexedAtomically() == null) + { + return true; + } + else + { + return m2Property.isIndexedAtomically(); + } + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.dictionary.PropertyDefinition#isTokenisedInIndex() + */ + public IndexTokenisationMode getIndexTokenisationMode() + { + if(m2Property.getIndexTokenisationMode() == null) + { + return IndexTokenisationMode.TRUE; + } + else + { + return m2Property.getIndexTokenisationMode(); + } + } + + public Facetable getFacetable() + { + if(m2Property.isFacetable() == null) + { + return Facetable.UNSET; + } + else + { + if(m2Property.isFacetable().booleanValue()) + { + return Facetable.TRUE; + } + else + { + return Facetable.FALSE; + } + } + } + + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getConstraints() + */ + public List getConstraints() + { + return constraintDefs; + } + + /* package */ M2ModelDiff diffProperty(PropertyDefinition propDef) + { + M2ModelDiff modelDiff = null; + boolean isUpdated = false; + boolean isUpdatedIncrementally = false; + + if (this == propDef) + { + modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_UNCHANGED); + return modelDiff; + } + + // check name - cannot be null + if (! name.equals(propDef.getName())) + { + isUpdated = true; + } + + // check title + if (! EqualsHelper.nullSafeEquals(getTitle(null), propDef.getTitle(null), false)) + { + isUpdatedIncrementally = true; + } + + // check description + if (! EqualsHelper.nullSafeEquals(getDescription(null), propDef.getDescription(null), false)) + { + isUpdatedIncrementally = true; + } + + // check default value + if (! EqualsHelper.nullSafeEquals(getDefaultValue(), propDef.getDefaultValue(), false)) + { + isUpdatedIncrementally = true; + } + + // check datatype qname (TODO check datatype defs separately) + if (! EqualsHelper.nullSafeEquals(getDataType().getName(), propDef.getDataType().getName())) + { + isUpdated = true; + } + + // check container class qname + if (! EqualsHelper.nullSafeEquals(getContainerClass().getName(), propDef.getContainerClass().getName())) + { + isUpdated = true; + } + + // check multi-valued + if (isMultiValued() != propDef.isMultiValued()) + { + isUpdated = true; + } + + // check mandatory + if (isMandatory() != propDef.isMandatory()) + { + isUpdated = true; + } + + // check mandatory enforced + if (isMandatoryEnforced() != propDef.isMandatoryEnforced()) + { + isUpdated = true; + } + + // check protected + if (isProtected() != propDef.isProtected()) + { + isUpdated = true; + } + + // + // property indexing - is index enabled -> stored, atomic, tokenized (true, false, both) + // + // + // true + // false + // both + // + // + + if (isIndexed() != propDef.isIndexed()) + { + isUpdated = true; + } + + if (isStoredInIndex() != propDef.isStoredInIndex()) + { + isUpdatedIncrementally = true; + } + + if (isIndexedAtomically() != propDef.isIndexedAtomically()) + { + isUpdatedIncrementally = true; + } + + if (! EqualsHelper.nullSafeEquals(getIndexTokenisationMode().toString(), propDef.getIndexTokenisationMode().toString(), false)) + { + isUpdated = true; + } + + + // check override + if (isOverride() != propDef.isOverride()) + { + isUpdated = true; + } + + // TODO - check prop constraints (inline and referenced) + + if (isUpdated) + { + modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_UPDATED); + } + else if (isUpdatedIncrementally) + { + modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_UPDATED_INC); + } + else + { + modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_UNCHANGED); + } + + return modelDiff; + } + + /*package*/ static Collection diffPropertyLists(Collection previousProperties, Collection newProperties) + { + List modelDiffs = new ArrayList(); + + for (PropertyDefinition previousProperty : previousProperties) + { + boolean found = false; + for (PropertyDefinition newProperty : newProperties) + { + if (newProperty.getName().equals(previousProperty.getName())) + { + modelDiffs.add(((M2PropertyDefinition)previousProperty).diffProperty(newProperty)); + found = true; + break; + } + } + + if (! found) + { + modelDiffs.add(new M2ModelDiff(previousProperty.getName(), M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_DELETED)); + } + } + + for (PropertyDefinition newProperty : newProperties) + { + boolean found = false; + for (PropertyDefinition previousProperty : previousProperties) + { + if (newProperty.getName().equals(previousProperty.getName())) + { + found = true; + break; + } + } + + if (! found) + { + modelDiffs.add(new M2ModelDiff(newProperty.getName(), M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_CREATED)); + } + } + + return modelDiffs; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getAnalyserResourceBundleName() + */ + @Override + public String getAnalyserResourceBundleName() + { + return analyserResourceBundleName; + } + + @Override + public String resolveAnalyserClassName() + { + return resolveAnalyserClassName(I18NUtil.getLocale()); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getAnalyserClassName(java.lang.String, java.util.Locale) + */ + @Override + public String resolveAnalyserClassName(Locale locale +) + { + ClassLoader resourceBundleClassLoader = getModel().getDictionaryDAO().getResourceClassLoader(); + if(resourceBundleClassLoader == null) + { + resourceBundleClassLoader = this.getClass().getClassLoader(); + } + + StringBuilder keyBuilder = new StringBuilder(64); + keyBuilder.append(getDataType().getModel().getName().toPrefixString()); + keyBuilder.append(".datatype"); + keyBuilder.append(".").append(getDataType().getName().toPrefixString()); + keyBuilder.append(".analyzer"); + String key = StringUtils.replace(keyBuilder.toString(), ":", "_"); + + String analyserClassName = null; + + String analyserResourceBundleName = getAnalyserResourceBundleName(); + if(analyserResourceBundleName != null) + { + ResourceBundle bundle = ResourceBundle.getBundle(analyserResourceBundleName, locale, resourceBundleClassLoader); + if(bundle.containsKey(key)) + { + analyserClassName = bundle.getString(key); + } + } + + // walk containing class and its hierarchy + + ClassDefinition classDefinition = null; + ClassDefinition parentClassDefinition = null; + while(analyserClassName == null) + { + if(classDefinition == null) + { + classDefinition = getContainerClass(); + } + else + { + if(parentClassDefinition == null) + { + break; + } + else + { + classDefinition = parentClassDefinition; + } + } + + parentClassDefinition = classDefinition.getParentClassDefinition(); + + analyserResourceBundleName = classDefinition.getAnalyserResourceBundleName(); + if(analyserResourceBundleName != null) + { + ResourceBundle bundle = ResourceBundle.getBundle(analyserResourceBundleName, locale, resourceBundleClassLoader); + if(bundle.containsKey(key)) + { + analyserClassName = bundle.getString(key); + } + } + if(analyserClassName == null) + { + if((parentClassDefinition == null) || !classDefinition.getModel().getName().equals(parentClassDefinition.getModel().getName())) + { + analyserResourceBundleName = classDefinition.getModel().getAnalyserResourceBundleName(); + if(analyserResourceBundleName != null) + { + ResourceBundle bundle = ResourceBundle.getBundle(analyserResourceBundleName, locale, resourceBundleClassLoader); + if(bundle.containsKey(key)) + { + analyserClassName = bundle.getString(key); + } + } + } + } + } + String defaultAnalyserResourceBundleName = this.getContainerClass().getModel().getDictionaryDAO().getDefaultAnalyserResourceBundleName(); + if(analyserClassName == null) + { + if(defaultAnalyserResourceBundleName != null) + { + ResourceBundle bundle = ResourceBundle.getBundle(defaultAnalyserResourceBundleName, locale, resourceBundleClassLoader); + if(bundle.containsKey(key)) + { + analyserClassName = bundle.getString(key); + } + } + } + if(analyserClassName == null) + { + analyserClassName = getDataType().resolveAnalyserClassName(locale); + } + return analyserClassName; + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2PropertyOverride.java b/src/main/java/org/alfresco/repo/dictionary/M2PropertyOverride.java new file mode 100644 index 0000000..b870396 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2PropertyOverride.java @@ -0,0 +1,110 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.List; +import java.util.Properties; + + +/** + * Property override definition + * + * @author David Caruana + * + */ +public class M2PropertyOverride +{ + private String name; + private Boolean isMandatory; + private Boolean isMandatoryEnforced; + private String defaultValue; + private List constraints; + private Properties configProperties = new Properties(); + + /*package*/ M2PropertyOverride() + { + } + + + public String getName() + { + return name; + } + + + public void setName(String name) + { + this.name = name; + } + + + public Boolean isMandatory() + { + return isMandatory; + } + + + public void setMandatory(Boolean isMandatory) + { + this.isMandatory = isMandatory; + } + + public Boolean isMandatoryEnforced() + { + return isMandatoryEnforced; + } + + public String getDefaultValue() + { + if (defaultValue != null && M2Class.PROPERTY_PLACEHOLDER.matcher(defaultValue).matches()) + { + String key = defaultValue.substring(defaultValue.indexOf("${") + 2, defaultValue.indexOf("}")); + String value = defaultValue.substring(defaultValue.indexOf("|") + 1); + + return configProperties.getProperty(key, value); + } + else + { + return defaultValue; + } + } + + + public void setDefaultValue(String defaultValue) + { + this.defaultValue = defaultValue; + } + + public List getConstraints() + { + return constraints; + } + + public void setConfigProperties(Properties configProperties) + { + this.configProperties = configProperties; + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2Type.java b/src/main/java/org/alfresco/repo/dictionary/M2Type.java new file mode 100644 index 0000000..50df31f --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2Type.java @@ -0,0 +1,41 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + + +/** + * Type Definition + * + * @author David Caruana + * + */ +public class M2Type extends M2Class +{ + /*package*/ M2Type() + { + super(); + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2TypeDefinition.java b/src/main/java/org/alfresco/repo/dictionary/M2TypeDefinition.java new file mode 100644 index 0000000..88b64e1 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2TypeDefinition.java @@ -0,0 +1,79 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.Map; + +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; + + +/** + * Compiled Type Definition + * + * @author David Caruana + */ +/*package*/ class M2TypeDefinition extends M2ClassDefinition + implements TypeDefinition +{ + /*package*/ M2TypeDefinition(ModelDefinition model, M2Type m2Type, NamespacePrefixResolver resolver, Map modelProperties, Map modelAssociations) + { + super(model, m2Type, resolver, modelProperties, modelAssociations); + } + + @Override + public String getDescription(MessageLookup messageLookup) + { + String value = M2Label.getLabel(model, messageLookup, "type", name, "description"); + + // if we don't have a description call the super class + if (value == null) + { + value = super.getDescription(messageLookup); + } + + return value; + } + + @Override + public String getTitle(MessageLookup messageLookup) + { + String value = M2Label.getLabel(model, messageLookup, "type", name, "title"); + + // if we don't have a title call the super class + if (value == null) + { + value = super.getTitle(messageLookup); + } + + return value; + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/M2XML.java b/src/main/java/org/alfresco/repo/dictionary/M2XML.java new file mode 100644 index 0000000..813d06c --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/M2XML.java @@ -0,0 +1,102 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.swing.text.StyledEditorKit.BoldAction; + +import org.alfresco.util.CachingDateFormat; + + +/** + * Support translating model from and to XML + * + * @author David Caruana + * + */ +public class M2XML +{ + + /** + * Convert XML date (of the form yyyy-MM-dd) to Date + * + * @param date the xml representation of the date + * @return the date + * @throws ParseException + */ + public static Date deserialiseDate(String date) + throws ParseException + { + Date xmlDate = null; + if (date != null) + { + SimpleDateFormat df = CachingDateFormat.getDateOnlyFormat(); + xmlDate = df.parse(date); + } + return xmlDate; + } + + + /** + * Convert date to XML date (of the form yyyy-MM-dd) + * + * @param date the date + * @return the xml representation of the date + */ + public static String serialiseDate(Date date) + { + String xmlDate = null; + if (date != null) + { + SimpleDateFormat df = CachingDateFormat.getDateOnlyFormat(); + xmlDate = df.format(date); + } + return xmlDate; + } + + public static Boolean deserialiseBoolean(String booleanString) throws ParseException + { + Boolean bool = null; + if((booleanString != null) && booleanString.length() > 0) + { + bool = Boolean.valueOf(booleanString); + } + return bool; + } + + public static String serialiseBoolean(Boolean bool) + { + String booleanString = null; + if (bool != null) + { + booleanString = bool.toString(); + } + return booleanString; + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/ModelQuery.java b/src/main/java/org/alfresco/repo/dictionary/ModelQuery.java new file mode 100644 index 0000000..d685e4a --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/ModelQuery.java @@ -0,0 +1,110 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.QName; + + +/** + * Access to model items. + * + * @author David Caruana + * + */ +/*package*/ interface ModelQuery +{ + /** + * Gets the specified data type + * + * @param name name of the data type + * @return data type definition + */ + public DataTypeDefinition getDataType(QName name); + + /** + * Gets the data type for the specified Java Class + * + * @param javaClass the java class + * @return the data type definition (or null, if mapping is not available) + */ + public DataTypeDefinition getDataType(Class javaClass); + + /** + * Gets the specified type + * + * @param name name of the type + * @return type definition + */ + public TypeDefinition getType(QName name); + + /** + * Gets the specified aspect + * + * @param name name of the aspect + * @return aspect definition + */ + public AspectDefinition getAspect(QName name); + + /** + * Gets the specified class + * + * @param name name of the class + * @return class definition + */ + public ClassDefinition getClass(QName name); + + /** + * Gets the specified property + * + * @param name name of the property + * @return property definition + */ + public PropertyDefinition getProperty(QName name); + + /** + * Gets the specified property constraint + * + * @param name the qualified name of the property constraint + * @return ConstraintDefinition + */ + public ConstraintDefinition getConstraint(QName name); + + /** + * Gets the specified association + * + * @param name name of the association + * @return association definition + */ + public AssociationDefinition getAssociation(QName name); + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/NamespaceDAO.java b/src/main/java/org/alfresco/repo/dictionary/NamespaceDAO.java new file mode 100644 index 0000000..9a9d805 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/NamespaceDAO.java @@ -0,0 +1,90 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import org.alfresco.service.namespace.NamespacePrefixResolver; + + +/** + * Namespace DAO Interface. + * + * This DAO is responsible for retrieving and creating Namespace definitions. + * + * @author David Caruana + */ +public interface NamespaceDAO extends NamespacePrefixResolver +{ + /** + * Add a namespace URI + * + * @param uri the namespace uri to add + */ + public void addURI(String uri); + + /** + * Remove the specified URI + * + * @param uri the uri to remove + */ + public void removeURI(String uri); + + /** + * Add a namespace prefix + * + * @param prefix the prefix + * @param uri the uri to prefix + */ + public void addPrefix(String prefix, String uri); + + /** + * Remove a namspace prefix + * + * @param prefix the prefix to remove + */ + public void removePrefix(String prefix); + + /** + * Initialise Namespaces + */ +// public void init(); + +// public void afterDictionaryInit(); + + /** + * Destroy Namespaces + */ +// public void destroy(); + + /** + * Register with the Dictionary + */ +// public void registerDictionary(DictionaryDAO dictionaryDAO); + + /** + * Clear NamespaceLocal + */ +// public void clearNamespaceLocal(); +} diff --git a/src/main/java/org/alfresco/repo/dictionary/TenantDictionaryRegistryImpl.java b/src/main/java/org/alfresco/repo/dictionary/TenantDictionaryRegistryImpl.java new file mode 100644 index 0000000..5823c5e --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/TenantDictionaryRegistryImpl.java @@ -0,0 +1,259 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary; + +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.NamespaceException; +import org.alfresco.service.namespace.QName; + +/** + * Tenant-specific dictionary registry. + * + * @author sglover + * + */ +public class TenantDictionaryRegistryImpl extends AbstractDictionaryRegistry +{ + private String tenantDomain; + + public TenantDictionaryRegistryImpl(DictionaryDAO dictionaryDAO, String tenantDomain) + { + super(dictionaryDAO); + this.tenantDomain = tenantDomain; + } + + public String getTenantDomain() + { + return tenantDomain; + } + + private DictionaryRegistry getParent() + { + return dictionaryDAO.getDictionaryRegistry(""); + } + + @Override + public QName putModelImpl(CompiledModel model) + { + if(getParent().modelExists(model.getModelDefinition().getName())) + { + throw new IllegalArgumentException("Cannot change core model: " + model); + } + QName ret = super.putModelImpl(model); + + return ret; + } + + @Override + public Map getCompiledModels(boolean includeInherited) + { + Map ret = new HashMap(); + ret.putAll(super.getCompiledModels(includeInherited)); + if(includeInherited) + { + ret.putAll(getParent().getCompiledModels(includeInherited)); + } + return ret; + } + + @Override + public List getModelsForUri(String uri) + { + List models = new LinkedList<>(); + List parentModels = getParent().getModelsForUri(uri); + models.addAll(parentModels); + models.addAll(getModelsForUriImpl(uri)); + return models; + } + + @Override + public CompiledModel getModel(QName name) + { + CompiledModel model = getModelImpl(name); + if(model == null) + { + // try parent + model = getParent().getModel(name); + } + return model; + } + + @Override + public void removeModel(QName modelName) + { + CompiledModel model = removeModelImpl(modelName); + if(model == null) + { + // try parent + getParent().removeModel(modelName); + } + } + + @Override + public AspectDefinition getAspect(QName aspectName) + { + AspectDefinition aspect = getAspectImpl(aspectName); + if(aspect == null) + { + // try parent + aspect = getParent().getAspect(aspectName); + } + + return aspect; + } + + @Override + public boolean isModelInherited(QName modelName) + { + return (getParent().getModel(modelName) != null); + } + + @Override + public Map getPrefixesCache() + { + Map prefixesCache = new HashMap(); + + Map parentPrefixes = getParent().getPrefixesCache(); + prefixesCache.putAll(parentPrefixes); + prefixesCache.putAll(getPrefixesCacheImpl()); + + return prefixesCache; + } + + @Override + public List getUrisCache() + { + List urisCache = new LinkedList(); + + List parentUris = getParent().getUrisCache(); + urisCache.addAll(parentUris); + urisCache.addAll(getUrisCacheImpl()); + + return urisCache; + } + + @Override + public Collection getPrefixes(String URI) + { + Collection prefixes = getParent().getPrefixes(URI); + prefixes.addAll(getPrefixesImpl(URI)); + return prefixes; + } + + @Override + public void addURI(String uri) + { + if(getParent().hasURI(uri)) + { + throw new NamespaceException("URI " + uri + " has already been defined"); + } + addURIImpl(uri); + } + + @Override + public void addPrefix(String prefix, String uri) + { + if(getParent().hasPrefix(prefix)) + { + throw new NamespaceException("Prefix " + prefix + " has already been defined"); + } + addPrefixImpl(prefix, uri); + } + + @Override + public void removeURI(String uri) + { + if(!removeURIImpl(uri)) + { + // try parent + getParent().removeURI(uri); + } + } + + @Override + public void removePrefix(String prefix) + { + if(!removePrefixImpl(prefix)) + { + // try parent + getParent().removePrefix(prefix); + } + } + + @Override + protected void initImpl() + { + long startTime = System.currentTimeMillis(); + + // populate the dictionary based on registered sources (only for core registry) + for (DictionaryListener dictionaryDeployer : dictionaryDAO.getDictionaryListeners()) + { + dictionaryDeployer.onDictionaryInit(); + } + + // notify registered listeners that dictionary has been initialised (population is complete) + for (DictionaryListener dictionaryListener : dictionaryDAO.getDictionaryListeners()) + { + dictionaryListener.afterDictionaryInit(); + } + + // Done + if (logger.isInfoEnabled()) + { + Map models = getCompiledModels(false); + logger.info("Init Tenant Dictionary: model count = "+(models != null ? models.size() : 0) + +" in "+(System.currentTimeMillis()-startTime)+" msecs ["+Thread.currentThread()+"]"); + } + } + + @Override + public TypeDefinition getType(QName typeName) + { + TypeDefinition type = getTypeImpl(typeName); + if(type == null) + { + // try parent + type = getParent().getType(typeName); + } + return type; + } + + @Override + public void removeImpl() + { + for(DictionaryListener listener : dictionaryDAO.getDictionaryListeners()) + { + listener.afterDictionaryDestroy(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/repo/dictionary/constraint/AbstractConstraint.java b/src/main/java/org/alfresco/repo/dictionary/constraint/AbstractConstraint.java new file mode 100644 index 0000000..ca30823 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/constraint/AbstractConstraint.java @@ -0,0 +1,210 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary.constraint; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.service.cmr.dictionary.Constraint; +import org.alfresco.service.cmr.dictionary.ConstraintException; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; + +/** + * Base services for constraints. + * + * @author Derek Hulley + */ +public abstract class AbstractConstraint implements Constraint +{ + public static final String ERR_PROP_NOT_SET = "d_dictionary.constraint.err.property_not_set"; + public static final String ERR_EVALUATE_EXCEPTION = "d_dictionary.constraint.err.evaluate_exception"; + + + /** The constraint name. May be useful in error messages */ + private String shortName; + private String title; + + private ConstraintRegistry registry; + + /** + * Sets the constraint name. Automatically called after construction. Please excuse the strange method name as we + * want the property name to begin with an underscore to avoid property name clashes. + * + * @param shortName String + * @deprecated + */ + public void set_shortName(String shortName) + { + setShortName(shortName); + } + + /** + * Sets the constraint name + * @param name String + */ + public void setShortName(String name) + { + this.shortName = name; + } + + /** + * Gets the constraint name. + * + * @return the constraint name. + */ + public String getShortName() + { + return this.shortName; + } + + /** + * Optionally specify the registry that will be used to register the constraint. + * This is used when instantiating constraints outside the dictionary. + * + * @param registry the constraint registry + */ + public void setRegistry(ConstraintRegistry registry) + { + this.registry = registry; + } + + public String getType() + { + return this.getClass().getName(); + } + + public void setTitle(String title) + { + this.title = title; + } + + public String getTitle() + { + return title; + } + + public Map getParameters() + { + return new HashMap(3); + } + + /** + * {@inheritDoc} + *

+ * Registers the constraint with the registry, if present. Call this method if + * you want the constraint to be auto-registered. + */ + public void initialize() + { + if (registry != null) + { + registry.register(shortName, this); + } + } + + /** + * Check that the given value is not null. + * + * @param name the name of the property + * @param value the value to check for null + * + * @throws DictionaryException if the the property is null + */ + protected void checkPropertyNotNull(String name, Object value) + { + if (value == null) + { + throw new DictionaryException(AbstractConstraint.ERR_PROP_NOT_SET, name, getShortName()); + } + } + + /** + * @see #evaluateSingleValue(Object) + * @see #evaluateCollection(Collection) + */ + @SuppressWarnings("unchecked") + public final void evaluate(Object value) + { + if (value == null) + { + // null values are never evaluated + return; + } + try + { + // ensure that we can handle collections + if (DefaultTypeConverter.INSTANCE.isMultiValued(value)) + { + Collection collection = DefaultTypeConverter.INSTANCE.getCollection(Object.class, value); + evaluateCollection(collection); + } + else + { + evaluateSingleValue(value); + } + } + catch (ConstraintException e) + { + // this can go + throw e; + } + catch (Throwable e) + { + throw new DictionaryException(AbstractConstraint.ERR_EVALUATE_EXCEPTION, this, e.getMessage()); + } + } + + /** + * Only override if there is some specific evaluation that needs to be performed on the + * collection as a whole. + * + * @param collection the collection of values to evaluate + * + * @see #evaluateSingleValue(Object) + */ + protected void evaluateCollection(Collection collection) + { + for (Object value : collection) + { + if (value == null) + { + // contract states that it will always pass + continue; + } + evaluateSingleValue(value); + } + } + + /** + * Support for evaluation of properties. The value passed in will never be a + * Collection and will never be null. + * + * @throws ConstraintException throw this when the evaluation fails + */ + protected abstract void evaluateSingleValue(Object value); +} diff --git a/src/main/java/org/alfresco/repo/dictionary/constraint/ConstraintRegistry.java b/src/main/java/org/alfresco/repo/dictionary/constraint/ConstraintRegistry.java new file mode 100644 index 0000000..946a0a0 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/constraint/ConstraintRegistry.java @@ -0,0 +1,94 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary.constraint; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.service.cmr.dictionary.Constraint; + +/** + * A registry of constraints. + * + * @author Derek Hulley + * @since 3.2 + */ +public class ConstraintRegistry +{ + private static ConstraintRegistry instance = new ConstraintRegistry(); + + private Map constraints; + + /** + * @return Returns the singleton + */ + public static ConstraintRegistry getInstance() + { + return instance; + } + + /** + * Private constructor + * @see #getInstance() + */ + private ConstraintRegistry() + { + constraints = new HashMap(13); + } + + /** + * Register the constraint by name + */ + public void register(String name, Constraint constraint) + { + if (this == instance) + { + constraints.put(name, constraint); + } + else + { + instance.register(name, constraint); + } + } + + /** + * Get the constraint by name + * + * @param name the name by which the constraint was registered + * @return Returns the constraint or null if it does not exist. + */ + public Constraint getConstraint(String name) + { + if (this == instance) + { + return constraints.get(name); + } + else + { + return instance.getConstraint(name); + } + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/constraint/ListOfValuesConstraint.java b/src/main/java/org/alfresco/repo/dictionary/constraint/ListOfValuesConstraint.java new file mode 100644 index 0000000..72565ec --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/constraint/ListOfValuesConstraint.java @@ -0,0 +1,301 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary.constraint; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.cmr.dictionary.ConstraintException; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.repository.datatype.TypeConversionException; +import org.springframework.extensions.surf.util.I18NUtil; +import org.springframework.util.StringUtils; + +/** + * Constraint implementation that ensures the value is one of a constrained + * list of values. By default, this constraint is case-sensitive. + * + * @see #setAllowedValues(List) + * @see #setCaseSensitive(boolean) + * + * @author Derek Hulley + */ +public class ListOfValuesConstraint extends AbstractConstraint +{ + private static final String LOV_CONSTRAINT_VALUE = "listconstraint"; + public static final String CONSTRAINT_TYPE = "LIST"; + + public static final String CASE_SENSITIVE_PARAM = "caseSensitive"; + public static final String ALLOWED_VALUES_PARAM = "allowedValues"; + public static final String SORTED_PARAM = "sorted"; + + private static final String ERR_NO_VALUES = "d_dictionary.constraint.list_of_values.no_values"; + private static final String ERR_NON_STRING = "d_dictionary.constraint.string_length.non_string"; + private static final String ERR_INVALID_VALUE = "d_dictionary.constraint.list_of_values.invalid_value"; + + private List allowedValues; + private List allowedValuesUpper; + private Set allowedValuesSet; + private Set allowedValuesUpperSet; + protected boolean caseSensitive; + protected boolean sorted; + + public ListOfValuesConstraint() + { + caseSensitive = true; + sorted = false; + allowedValues = Collections.emptyList(); + allowedValuesUpper = Collections.emptyList(); + allowedValuesSet = Collections.emptySet(); + allowedValuesUpperSet = Collections.emptySet(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getType() + { + return CONSTRAINT_TYPE; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(80); + sb.append("ListOfValuesConstraint") + .append("[ allowedValues=").append(allowedValues) + .append(", caseSensitive=").append(caseSensitive) + .append(", sorted=").append(sorted) + .append("]"); + return sb.toString(); + } + + /** + * Get the allowed values. Note that these are String instances, but may + * represent non-String values. It is up to the caller to distinguish. + * + * Sorts list if appropriate. + * + * @return Returns the values allowed + */ + public List getAllowedValues() + { + List rawValues = getRawAllowedValues(); + if (sorted == true) + { + List values = new ArrayList(rawValues); + Collections.sort(values); + return values; + } + else + { + return rawValues; + } + } + + /** + * Get the allowed values. Note that these are String instances, but may + * represent non-String values. It is up to the caller to distinguish. + * + * @return Returns the values allowed + */ + protected List getRawAllowedValues() + { + return allowedValues; + } + + /** + * Get the display label for the specified allowable value in this constraint. + * A key is constructed as follows: + *
+     *   "listconstraint." + constraintName + "." + constraintAllowableValue.
+     *   e.g. listconstraint.test_listConstraintOne.VALUE_ONE.
+     * 
+ * This key is then used to look up a properties bundle for the localised display label. + * Spaces are allowed in the keys, but they should be escaped in the properties file as follows: + *
+     * listconstraint.test_listConstraintOne.VALUE\ WITH\ SPACES=Display label
+     * 
+ * + * @param constraintAllowableValue String + * @param messageLookup MessageLookup + * @return the localised display label for the specified constraint value in the current locale. + * If no localisation is defined, it will return the allowed value itself. + * If the specified allowable value is not in the model, returns null. + * @since 4.0 + * @see I18NUtil#getLocale() + */ + public String getDisplayLabel(String constraintAllowableValue, MessageLookup messageLookup) + { + if (!allowedValues.contains(constraintAllowableValue)) + { + return null; + } + + String key = LOV_CONSTRAINT_VALUE; + key += "." + this.getShortName(); + key += "." + constraintAllowableValue; + key = StringUtils.replace(key, ":", "_"); + + String message = messageLookup.getMessage(key, I18NUtil.getLocale()); + return message == null ? constraintAllowableValue : message; + } + + + /** + * Set the values that are allowed by the constraint. + * + * @param allowedValues a list of allowed values + */ + public void setAllowedValues(List allowedValues) + { + if (allowedValues == null) + { + throw new DictionaryException(ERR_NO_VALUES); + } + int valueCount = allowedValues.size(); + if (valueCount == 0) + { + throw new DictionaryException(ERR_NO_VALUES); + } + this.allowedValues = Collections.unmodifiableList(allowedValues); + this.allowedValuesSet = new HashSet(allowedValues); + // make the upper case versions + this.allowedValuesUpper = new ArrayList(valueCount); + this.allowedValuesUpperSet = new HashSet(valueCount); + for (String allowedValue : this.allowedValues) + { + String allowedValueUpper = allowedValue.toUpperCase(); + allowedValuesUpper.add(allowedValueUpper); + allowedValuesUpperSet.add(allowedValueUpper); + } + } + + /** + * @return Returns true if this constraint is case-sensitive (default) + */ + public boolean isCaseSensitive() + { + return caseSensitive; + } + + /** + * Set the handling of case checking. + * + * @param caseSensitive true if the constraint is case-sensitive (default), + * or false for case-insensitive. + */ + public void setCaseSensitive(boolean caseSensitive) + { + this.caseSensitive = caseSensitive; + } + + /** + * Indicates whether the list of values are sorted or not. + * + * @return true if sorted, false otherwise + */ + public boolean isSorted() + { + return sorted; + } + + /** + * Set whether the values are ordered or not. + * + * @param sorted true if sorted, false otherwise + */ + public void setSorted(boolean sorted) + { + this.sorted = sorted; + } + + /** + * @see org.alfresco.repo.dictionary.constraint.AbstractConstraint#initialize() + */ + @Override + public void initialize() + { + super.initialize(); + checkPropertyNotNull(ALLOWED_VALUES_PARAM, allowedValues); + } + + /** + * @see org.alfresco.repo.dictionary.constraint.AbstractConstraint#getParameters() + */ + @Override + public Map getParameters() + { + Map params = new HashMap(2); + + params.put(CASE_SENSITIVE_PARAM, this.caseSensitive); + params.put(ALLOWED_VALUES_PARAM, this.allowedValues); + params.put(SORTED_PARAM, this.sorted); + + return params; + } + + /** + * @see org.alfresco.repo.dictionary.constraint.AbstractConstraint#evaluateSingleValue(java.lang.Object) + */ + @Override + protected void evaluateSingleValue(Object value) + { + // convert the value to a String + String valueStr = null; + try + { + valueStr = DefaultTypeConverter.INSTANCE.convert(String.class, value); + } + catch (TypeConversionException e) + { + throw new ConstraintException(ERR_NON_STRING, value); + } + // check that the value is in the set of allowed values + if (caseSensitive) + { + if (!allowedValuesSet.contains(valueStr)) + { + throw new ConstraintException(ERR_INVALID_VALUE, value); + } + } + else + { + if (!allowedValuesUpperSet.contains(valueStr.toUpperCase())) + { + throw new ConstraintException(ERR_INVALID_VALUE, value); + } + } + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/constraint/NameChecker.java b/src/main/java/org/alfresco/repo/dictionary/constraint/NameChecker.java new file mode 100644 index 0000000..d8cd8a8 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/constraint/NameChecker.java @@ -0,0 +1,102 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary.constraint; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.dictionary.Constraint; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.PropertyCheck; +import org.springframework.beans.factory.InitializingBean; + +/** + * Loads cm:filename constraint from dictionary to use it during batch jobs + * + * @see MNT-9414 + * + * @author Viachaslau Tsikhanovich * + */ +public class NameChecker implements InitializingBean +{ + private DictionaryService dictionaryService; + + private Constraint nameConstraint; + + // namespaceURI of the constraint + private String namespaceURI = NamespaceService.CONTENT_MODEL_1_0_URI; + + //constraint local name + private String constraintLocalName ="filename"; + + public void setNamespaceURI(String namespaceURI) + { + this.namespaceURI = namespaceURI; + } + + public void setConstraintLocalName(String constraintLocalName) + { + this.constraintLocalName = constraintLocalName; + } + + public DictionaryService getDictionaryService() + { + return dictionaryService; + } + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * Loads filename constraint from dictionary + */ + @Override + public void afterPropertiesSet() throws Exception + { + PropertyCheck.mandatory(this, "dictionaryService", dictionaryService); + + QName qNameConstraint = QName.createQName(namespaceURI, constraintLocalName); + ConstraintDefinition constraintDef = dictionaryService.getConstraint(qNameConstraint); + if (constraintDef == null) + { + throw new AlfrescoRuntimeException("Constraint definition does not exist: " + qNameConstraint); + } + nameConstraint = constraintDef.getConstraint(); + if (nameConstraint == null) + { + throw new AlfrescoRuntimeException("Constraint does not exist: " + qNameConstraint); + } + } + + public void evaluate(Object value) + { + nameConstraint.evaluate(value); + } + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/constraint/NoOpConstraint.java b/src/main/java/org/alfresco/repo/dictionary/constraint/NoOpConstraint.java new file mode 100644 index 0000000..ae14340 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/constraint/NoOpConstraint.java @@ -0,0 +1,41 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary.constraint; + +/** + * A No operation constraint, it does nothing + * @author Gethin James + */ +public class NoOpConstraint extends AbstractConstraint +{ + + @Override + protected void evaluateSingleValue(Object value) + { + //No operation, it does nothing + } + +} diff --git a/src/main/java/org/alfresco/repo/dictionary/constraint/NumericRangeConstraint.java b/src/main/java/org/alfresco/repo/dictionary/constraint/NumericRangeConstraint.java new file mode 100644 index 0000000..c0f6618 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/constraint/NumericRangeConstraint.java @@ -0,0 +1,156 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary.constraint; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.service.cmr.dictionary.ConstraintException; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; + +/** + * Constraint implementation that ensures that the value is a numeric value bewteen a given + * minimum and maximum value. If a minimum or maximum value are not provided, then the JAVA + * Double's {@link Double#MIN_VALUE minimum value} or {@link Double#MAX_VALUE maximum value} + * are assumed. + * + * @see #setMinValue(double) + * @see #setMaxValue(double) + * @see java.lang.Double#parseDouble(java.lang.String) + * + * @author Derek Hulley + */ +public class NumericRangeConstraint extends AbstractConstraint +{ + private static final String ERR_INVALID_MIN_VALUE = "d_dictionary.constraint.numeric_range.invalid_min_value"; + private static final String ERR_INVALID_MAX_VALUE = "d_dictionary.constraint.numeric_range.invalid_max_value"; + private static final String ERR_NON_NUMERIC = "d_dictionary.constraint.numeric_range.non_numeric"; + private static final String ERR_OUT_OF_RANGE = "d_dictionary.constraint.numeric_range.out_of_range"; + + private double minValue = Double.MIN_VALUE; + private double maxValue = Double.MAX_VALUE; + + @Override + public String getType() + { + return "MINMAX"; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(80); + sb.append("NumericRangeConstraint") + .append("[ minValue=").append(minValue) + .append(", maxValue=").append(maxValue) + .append("]"); + return sb.toString(); + } + + /** + * @return Returns the minimum value allowed by the constraint + */ + public double getMinValue() + { + return minValue; + } + + /** + * Set the minimum value allowed, which can be any value between + * {@link Double#MIN_VALUE} and {@link Double#MAX_VALUE}. + * + * @param minValue the minimum value allowed by the constraint + */ + public void setMinValue(double minValue) + { + if (minValue > this.maxValue) + { + throw new DictionaryException(ERR_INVALID_MIN_VALUE, minValue); + } + this.minValue = minValue; + } + + /** + * @return Returns the minimum value allowed by the constraint + */ + public double getMaxValue() + { + return maxValue; + } + + /** + * Set the maximum value allowed, which can be any value between + * {@link Double#MIN_VALUE} and {@link Double#MAX_VALUE}. + * + * @param maxValue the minimum value allowed by the constraint + */ + public void setMaxValue(double maxValue) + { + if (maxValue < this.minValue) + { + throw new DictionaryException(ERR_INVALID_MAX_VALUE, maxValue); + } + this.maxValue = maxValue; + } + + @Override + public Map getParameters() + { + Map params = new HashMap(2); + + params.put("minValue", this.minValue); + params.put("maxValue", this.maxValue); + + return params; + } + + protected void evaluateSingleValue(Object value) + { + // ensure that the value can be converted to a double + double checkValue = Double.NaN; + try + { + checkValue = DefaultTypeConverter.INSTANCE.doubleValue(value); + } + catch (NumberFormatException e) + { + throw new ConstraintException(ERR_NON_NUMERIC, value); + } + + // Infinity and NaN cannot match + if (Double.isInfinite(checkValue) || Double.isNaN(checkValue)) + { + throw new ConstraintException(ERR_OUT_OF_RANGE, checkValue, minValue, maxValue); + } + + // Check that the value is in range + if (checkValue > maxValue || checkValue < minValue) + { + throw new ConstraintException(ERR_OUT_OF_RANGE, checkValue, minValue, maxValue); + } + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/constraint/RegexConstraint.java b/src/main/java/org/alfresco/repo/dictionary/constraint/RegexConstraint.java new file mode 100644 index 0000000..a8721fd --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/constraint/RegexConstraint.java @@ -0,0 +1,162 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary.constraint; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.springframework.extensions.surf.util.I18NUtil; +import org.alfresco.service.cmr.dictionary.ConstraintException; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; + +/** + * Constraint implementation that performs regular expression comparisons. + * Where possible, the {@link org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter type converter} + * will be used to first convert the value to a String, so the evaluation + * will be against the value's String equivalent. + *

+ * The failure condition can be changed to occur either on a match or on a non-match by using + * the {@link #setRequiresMatch(boolean) requiresMatch} property. The default is true, i.e. + * failures will occur if the object value does not match the given expression. + * + * @see java.lang.String#matches(java.lang.String) + * @see java.util.regex.Pattern + * + * @author Derek Hulley + */ +public class RegexConstraint extends AbstractConstraint +{ + public static final String CONSTRAINT_REGEX_NO_MATCH = "d_dictionary.constraint.regex.no_match"; + public static final String CONSTRAINT_REGEX_MATCH = "d_dictionary.constraint.regex.match"; + public static final String CONSTRAINT_REGEX_MSG_PREFIX = "d_dictionary.constraint.regex.error."; + + private String expression; + private Pattern patternMatcher; + private boolean requiresMatch = true; + + /** + * {@inheritDoc} + */ + public String getType() + { + return "REGEX"; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(80); + sb.append("RegexConstraint") + .append("[ expression=").append(expression) + .append(", requiresMatch=").append(requiresMatch) + .append("]"); + return sb.toString(); + } + + /** + * @return Returns the regular expression similar to the {@link String#matches(java.lang.String)} + */ + public String getExpression() + { + return expression; + } + + /** + * Set the regular expression used to evaluate String values + * @param expression regular expression similar to the {@link String#matches(java.lang.String)} argument + */ + public void setExpression(String expression) + { + this.expression = expression; + } + + /** + * @return Returns true if the value must match the regular expression + * or false if the value must not match the regular expression + */ + public boolean getRequiresMatch() + { + return requiresMatch; + } + + /** + * Set whether the regular expression must be matched or not + * + * @param requiresMatch Set to true if the value must match the regular expression + * or false if the value must not match the regular expression + */ + public void setRequiresMatch(boolean requiresMatch) + { + this.requiresMatch = requiresMatch; + } + + @Override + public Map getParameters() + { + Map params = new HashMap(2); + + params.put("expression", this.expression); + params.put("requiresMatch", this.requiresMatch); + + return params; + } + + @Override + public void initialize() + { + checkPropertyNotNull("expression", expression); + + this.patternMatcher = Pattern.compile(expression); + } + + protected void evaluateSingleValue(Object value) + { + // convert the value to a String + String valueStr = DefaultTypeConverter.INSTANCE.convert(String.class, value); + Matcher matcher = patternMatcher.matcher(valueStr); + boolean matches = matcher.matches(); + if (matches != requiresMatch) + { + // Look for a message corresponding to this constraint name + String messageId = CONSTRAINT_REGEX_MSG_PREFIX + getShortName(); + if (I18NUtil.getMessage(messageId, value) != null) + { + throw new ConstraintException(messageId, value); + } + // Otherwise, fall back to a generic (but unfriendly) message + else if (requiresMatch) + { + throw new ConstraintException(RegexConstraint.CONSTRAINT_REGEX_NO_MATCH, value, expression); + } + else + { + throw new ConstraintException(RegexConstraint.CONSTRAINT_REGEX_MATCH, value, expression); + } + } + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/constraint/RegisteredConstraint.java b/src/main/java/org/alfresco/repo/dictionary/constraint/RegisteredConstraint.java new file mode 100644 index 0000000..ac59c75 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/constraint/RegisteredConstraint.java @@ -0,0 +1,133 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary.constraint; + +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.dictionary.Constraint; +import org.alfresco.service.cmr.dictionary.DictionaryException; + +/** + * Constraint implementation that defers to constraints registered with the + * static instance of the {@link ConstraintRegistry}. + * + * @author Derek Hulley + */ +public final class RegisteredConstraint implements Constraint +{ + private static final String ERR_NAME_NOT_REGISTERED = "d_dictionary.constraint.registered.not_registered"; + + private String shortName; + private String registeredName; + + public RegisteredConstraint() + { + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(80); + sb.append("RegisteredConstraint") + .append("[ registeredName=").append(registeredName) + .append(", constraint=").append(ConstraintRegistry.getInstance().getConstraint(registeredName)) + .append("]"); + return sb.toString(); + } + + public String getShortName() + { + return shortName; + } + + public void setShortName(String shortName) + { + this.shortName = shortName; + } + + /** + * Set the name of the constraint that will be used to look up the constraint + * that will be delegated to. + */ + public void setRegisteredName(String registeredName) + { + this.registeredName = registeredName; + } + + public void initialize() + { + if (registeredName == null) + { + throw new DictionaryException(AbstractConstraint.ERR_PROP_NOT_SET, "registeredName"); + } + } + + /** + * @return the constraint that matches the registered name + */ + public Constraint getRegisteredConstraint() + { + Constraint constraint = ConstraintRegistry.getInstance().getConstraint(registeredName); + if (constraint == null) + { + throw new DictionaryException(ERR_NAME_NOT_REGISTERED, registeredName); + } + return constraint; + } + + /** + * Defers to the registered constraint + */ + public String getType() + { + return getRegisteredConstraint().getType(); + } + + /** + * Defers to the registered constraint + */ + public String getTitle() + { + return getRegisteredConstraint().getTitle(); + } + + /** + * Defers to the registered constraint + */ + public Map getParameters() + { + return getRegisteredConstraint().getParameters(); + } + + /** + * Defers to the registered constraint + */ + public void evaluate(Object value) + { + getRegisteredConstraint().evaluate(value); + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/constraint/StringLengthConstraint.java b/src/main/java/org/alfresco/repo/dictionary/constraint/StringLengthConstraint.java new file mode 100644 index 0000000..003c921 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/constraint/StringLengthConstraint.java @@ -0,0 +1,155 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.dictionary.constraint; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.service.cmr.dictionary.ConstraintException; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.repository.datatype.TypeConversionException; + +/** + * Constraint implementation that ensures that the length of the String value. + * + * @see #setMinLength(int) + * @see #setMaxLength(int) + * + * @author Derek Hulley + */ +public class StringLengthConstraint extends AbstractConstraint +{ + private static final String ERR_INVALID_MIN_LENGTH = "d_dictionary.constraint.string_length.invalid_min_length"; + private static final String ERR_INVALID_MAX_LENGTH = "d_dictionary.constraint.string_length.invalid_max_length"; + private static final String ERR_NON_STRING = "d_dictionary.constraint.string_length.non_string"; + private static final String ERR_INVALID_LENGTH = "d_dictionary.constraint.string_length.invalid_length"; + + private int minLength = 0; + private int maxLength = Integer.MAX_VALUE; + + /** + * {@inheritDoc} + */ + @Override + public String getType() + { + return "LENGTH"; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(80); + sb.append("StringLengthConstraint") + .append("[ minLength=").append(minLength) + .append(", maxLength=").append(maxLength) + .append("]"); + return sb.toString(); + } + + /** + * @return Returns the minimum number of characters allowed + */ + public int getMinLength() + { + return minLength; + } + + /** + * Set the minimum number of characters allowed. Valid values are in + * the range [0, {@link Integer#MAX_VALUE}]. + * + * @param minLength the minimum numbers of characters allowed + */ + public void setMinLength(int minLength) + { + if (minLength > this.maxLength || minLength < 0) + { + throw new DictionaryException(ERR_INVALID_MIN_LENGTH, minLength); + } + this.minLength = minLength; + } + + /** + * @return Returns the maximum number of characters allowed + */ + public int getMaxLength() + { + return maxLength; + } + + /** + * Set the maximum number of characters allowed. Valid values are in + * the range [0, {@link Integer#MAX_VALUE}]. + * + * @param maxLength the minimum numbers of characters allowed + */ + public void setMaxLength(int maxLength) + { + if (maxLength < this.minLength) + { + throw new DictionaryException(ERR_INVALID_MAX_LENGTH, maxLength); + } + this.maxLength = maxLength; + } + + @Override + public Map getParameters() + { + Map params = new HashMap(2); + + params.put("minLength", this.minLength); + params.put("maxLength", this.maxLength); + + return params; + } + + protected void evaluateSingleValue(Object value) + { + // ensure that the value can be converted to a String + String checkValue = null; + try + { + checkValue = DefaultTypeConverter.INSTANCE.convert(String.class, value); + } + catch (TypeConversionException e) + { + throw new ConstraintException(ERR_NON_STRING, value); + } + + // Check that the value length + int length = checkValue.length(); + if (length > maxLength || length < minLength) + { + if (length > 20) + { + checkValue = checkValue.substring(0, 17) + "..."; + } + throw new ConstraintException(ERR_INVALID_LENGTH, checkValue, minLength, maxLength); + } + } +} diff --git a/src/main/java/org/alfresco/repo/dictionary/m2binding.xml b/src/main/java/org/alfresco/repo/dictionary/m2binding.xml new file mode 100644 index 0000000..98fc441 --- /dev/null +++ b/src/main/java/org/alfresco/repo/dictionary/m2binding.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/org/alfresco/repo/i18n/StaticMessageLookup.java b/src/main/java/org/alfresco/repo/i18n/StaticMessageLookup.java new file mode 100644 index 0000000..b605394 --- /dev/null +++ b/src/main/java/org/alfresco/repo/i18n/StaticMessageLookup.java @@ -0,0 +1,84 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.i18n; + +import java.util.Locale; + +import org.alfresco.service.NotAuditable; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * A {@link MessageLookup} that retrieves messages from a resource bundle in the classpath. + */ +public class StaticMessageLookup implements MessageLookup +{ + + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.i18n.MessageLookup#getMessage(java.lang.String) + */ + @Override + @NotAuditable + public String getMessage(String messageKey) + { + return I18NUtil.getMessage(messageKey); + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.i18n.MessageLookup#getMessage(java.lang.String, java.util.Locale) + */ + @Override + @NotAuditable + public String getMessage(String messageKey, Locale locale) + { + return I18NUtil.getMessage(messageKey, locale); + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.i18n.MessageLookup#getMessage(java.lang.String, java.lang.Object[]) + */ + @Override + @NotAuditable + public String getMessage(String messageKey, Object... params) + { + return I18NUtil.getMessage(messageKey, params); + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.i18n.MessageLookup#getMessage(java.lang.String, java.util.Locale, + * java.lang.Object[]) + */ + @Override + @NotAuditable + public String getMessage(String messageKey, Locale locale, Object... params) + { + return I18NUtil.getMessage(messageKey, locale, params); + } +} diff --git a/src/main/java/org/alfresco/repo/index/shard/Floc.java b/src/main/java/org/alfresco/repo/index/shard/Floc.java new file mode 100644 index 0000000..d0e1800 --- /dev/null +++ b/src/main/java/org/alfresco/repo/index/shard/Floc.java @@ -0,0 +1,230 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.index.shard; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; + +import org.alfresco.service.cmr.repository.StoreRef; + +/** + * @author Andy + * + */ +public class Floc implements Serializable +{ + private static final long serialVersionUID = 3198471269440656171L; + + private HashSet storeRefs = new HashSet(); + + private int numberOfShards; + + ShardMethodEnum shardMethod; + + private String template; + + private boolean hasContent; + + private HashMap propertyBag = new HashMap(); + + /** + * + */ + public Floc() + { + } + + /** + * @return the storeRefs + */ + public HashSet getStoreRefs() + { + return storeRefs; + } + + /** + * @param storeRefs the storeRefs to set + */ + public void setStoreRefs(HashSet storeRefs) + { + this.storeRefs = storeRefs; + } + + /** + * @return the numberOfShards + */ + public int getNumberOfShards() + { + return numberOfShards; + } + + /** + * @param numberOfShards the numberOfShards to set + */ + public void setNumberOfShards(int numberOfShards) + { + this.numberOfShards = numberOfShards; + } + + /** + * @return the shardMethod + */ + public ShardMethodEnum getShardMethod() + { + return shardMethod; + } + + /** + * @param shardMethod the shardMethod to set + */ + public void setShardMethod(ShardMethodEnum shardMethod) + { + this.shardMethod = shardMethod; + } + + /** + * @return the template + */ + public String getTemplate() + { + return template; + } + + /** + * @param template the template to set + */ + public void setTemplate(String template) + { + this.template = template; + } + + /** + * @return the hasContent + */ + public boolean hasContent() + { + return hasContent; + } + + /** + * @param hasContent the hasContent to set + */ + public void setHasContent(boolean hasContent) + { + this.hasContent = hasContent; + } + + /** + * @return the propertyBag + */ + public HashMap getPropertyBag() + { + return propertyBag; + } + + /** + * @param propertyBag the propertyBag to set + */ + public void setPropertyBag(HashMap propertyBag) + { + this.propertyBag = propertyBag; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + (hasContent ? 1231 : 1237); + result = prime * result + numberOfShards; + result = prime * result + ((propertyBag == null) ? 0 : propertyBag.hashCode()); + result = prime * result + ((shardMethod == null) ? 0 : shardMethod.hashCode()); + result = prime * result + ((storeRefs == null) ? 0 : storeRefs.hashCode()); + result = prime * result + ((template == null) ? 0 : template.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Floc other = (Floc) obj; + if (hasContent != other.hasContent) + return false; + if (numberOfShards != other.numberOfShards) + return false; + if (propertyBag == null) + { + if (other.propertyBag != null) + return false; + } + else if (!propertyBag.equals(other.propertyBag)) + return false; + if (shardMethod != other.shardMethod) + return false; + if (storeRefs == null) + { + if (other.storeRefs != null) + return false; + } + else if (!storeRefs.equals(other.storeRefs)) + return false; + if (template == null) + { + if (other.template != null) + return false; + } + else if (!template.equals(other.template)) + return false; + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return "Floc [storeRefs=" + + storeRefs + ", numberOfShards=" + numberOfShards + ", shardMethod=" + shardMethod + ", template=" + template + ", hasContent=" + hasContent + ", propertyBag=" + + propertyBag + "]"; + } + + + +} diff --git a/src/main/java/org/alfresco/repo/index/shard/Shard.java b/src/main/java/org/alfresco/repo/index/shard/Shard.java new file mode 100644 index 0000000..72f6f94 --- /dev/null +++ b/src/main/java/org/alfresco/repo/index/shard/Shard.java @@ -0,0 +1,128 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.index.shard; + +import java.io.Serializable; + +/** + * @author Andy + * + */ +public class Shard implements Serializable +{ + private static final long serialVersionUID = -7255962796619754211L; + + private Floc floc; + + private int instance; + + public Shard() + { + + } + + /** + * @return the floc + */ + public Floc getFloc() + { + return floc; + } + + /** + * @param floc the floc to set + */ + public void setFloc(Floc floc) + { + this.floc = floc; + } + + /** + * @return the instance + */ + public int getInstance() + { + return instance; + } + + /** + * @param instance the instance to set + */ + public void setInstance(int instance) + { + this.instance = instance; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((floc == null) ? 0 : floc.hashCode()); + result = prime * result + instance; + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Shard other = (Shard) obj; + if (floc == null) + { + if (other.floc != null) + return false; + } + else if (!floc.equals(other.floc)) + return false; + if (instance != other.instance) + return false; + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return "Shard [floc=" + floc + ", instance=" + instance + "]"; + } + + + +} diff --git a/src/main/java/org/alfresco/repo/index/shard/ShardInstance.java b/src/main/java/org/alfresco/repo/index/shard/ShardInstance.java new file mode 100644 index 0000000..348602d --- /dev/null +++ b/src/main/java/org/alfresco/repo/index/shard/ShardInstance.java @@ -0,0 +1,187 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.index.shard; + +import java.io.Serializable; + + +/** + * @author Andy + * + */ +public class ShardInstance implements Serializable +{ + private static final long serialVersionUID = -3407675092111933581L; + + private Shard shard; + + private String baseUrl; + + private int port; + + private String hostName; + + public ShardInstance() + { + } + + /** + * @return the shard + */ + public Shard getShard() + { + return shard; + } + + /** + * @param shard the shard to set + */ + public void setShard(Shard shard) + { + this.shard = shard; + } + + /** + * @return the baseUrl + */ + public String getBaseUrl() + { + return baseUrl; + } + + /** + * @param baseUrl the baseUrl to set + */ + public void setBaseUrl(String baseUrl) + { + if((baseUrl != null) && baseUrl.endsWith("/")) + { + this.baseUrl = baseUrl.substring(0, baseUrl.length()-1); + } + else + { + this.baseUrl = baseUrl; + } + } + + /** + * @return the port + */ + public int getPort() + { + return port; + } + + /** + * @param port the port to set + */ + public void setPort(int port) + { + this.port = port; + } + + /** + * @return the hostName + */ + public String getHostName() + { + return hostName; + } + + /** + * @param hostName the hostName to set + */ + public void setHostName(String hostName) + { + this.hostName = hostName; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((baseUrl == null) ? 0 : baseUrl.hashCode()); + result = prime * result + ((hostName == null) ? 0 : hostName.hashCode()); + result = prime * result + port; + result = prime * result + ((shard == null) ? 0 : shard.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ShardInstance other = (ShardInstance) obj; + if (baseUrl == null) + { + if (other.baseUrl != null) + return false; + } + else if (!baseUrl.equals(other.baseUrl)) + return false; + if (hostName == null) + { + if (other.hostName != null) + return false; + } + else if (!hostName.equals(other.hostName)) + return false; + if (port != other.port) + return false; + if (shard == null) + { + if (other.shard != null) + return false; + } + else if (!shard.equals(other.shard)) + return false; + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return "ShardInstance [shard=" + shard + ", baseUrl=" + baseUrl + ", port=" + port + ", hostName=" + hostName + "]"; + } + + + +} diff --git a/src/main/java/org/alfresco/repo/index/shard/ShardMethodEnum.java b/src/main/java/org/alfresco/repo/index/shard/ShardMethodEnum.java new file mode 100644 index 0000000..967f8df --- /dev/null +++ b/src/main/java/org/alfresco/repo/index/shard/ShardMethodEnum.java @@ -0,0 +1,78 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.index.shard; +/** + * Enum that details sharding type + * @author Andy + * @author Michael Suzuki + * + */ +public enum ShardMethodEnum +{ + MOD_ACL_ID, + ACL_ID, + DB_ID, + DATE,//Time stamp + DATE_YEAR, + DATE_MONTH, + UNKOWN; + + public static ShardMethodEnum getShardMethod(String shardMethod) + { + if(shardMethod == null) + { + return UNKOWN; + } + ShardMethodEnum shardMethodEnum; + switch (shardMethod) + { + //MOD_ACL_ID legacy acl used in Alfresco 5.1 + case "MOD_ACL_ID": + shardMethodEnum = MOD_ACL_ID; + break; + //ACL id based on murmur hash. + case "ACL_ID": + shardMethodEnum = ACL_ID; + break; + case "DB_ID": + shardMethodEnum = DB_ID; + break; + case "DATE": + shardMethodEnum = DATE; + break; + case "DATE_YEAR": + shardMethodEnum = DATE_YEAR; + break; + case "DATE_MONTH": + shardMethodEnum = DATE_MONTH; + break; + default: + shardMethodEnum = UNKOWN; + break; + } + return shardMethodEnum; + } +} diff --git a/src/main/java/org/alfresco/repo/index/shard/ShardState.java b/src/main/java/org/alfresco/repo/index/shard/ShardState.java new file mode 100644 index 0000000..133c5bb --- /dev/null +++ b/src/main/java/org/alfresco/repo/index/shard/ShardState.java @@ -0,0 +1,209 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.repo.index.shard; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; + +import org.alfresco.service.cmr.repository.StoreRef; + +/** + * Store shard state for auto discovery + * + * @author Andy + * + */ +public class ShardState implements Serializable +{ + private static final long serialVersionUID = -5961621567026938963L; + + ShardInstance shardInstance; + + private boolean isMaster; + + private long lastUpdated; + + private long lastIndexedChangeSetId; + + private long lastIndexedTxCommitTime = 0; + + private long lastIndexedTxId = 0; + + private long lastIndexedChangeSetCommitTime = 0; + + + private HashMap propertyBag = new HashMap(); + + public ShardState() + { + } + + /** + * @return the shardInstance + */ + public ShardInstance getShardInstance() + { + return shardInstance; + } + + /** + * @param shardInstance the shardInstance to set + */ + public void setShardInstance(ShardInstance shardInstance) + { + this.shardInstance = shardInstance; + } + + /** + * @return the isMaster + */ + public boolean isMaster() + { + return isMaster; + } + + /** + * @param isMaster the isMaster to set + */ + public void setMaster(boolean isMaster) + { + this.isMaster = isMaster; + } + + /** + * @return the propertyBag + */ + public HashMap getPropertyBag() + { + return propertyBag; + } + + /** + * @param propertyBag the propertyBag to set + */ + public void setPropertyBag(HashMap propertyBag) + { + this.propertyBag = propertyBag; + } + + + /** + * @return the lastUpdated + */ + public long getLastUpdated() + { + return lastUpdated; + } + + /** + * @param lastUpdated the lastUpdated to set + */ + public void setLastUpdated(long lastUpdated) + { + this.lastUpdated = lastUpdated; + } + + /** + * @return the lastIndexedChangeSetId + */ + public long getLastIndexedChangeSetId() + { + return lastIndexedChangeSetId; + } + + /** + * @param lastIndexedChangeSetId the lastIndexedChangeSetId to set + */ + public void setLastIndexedChangeSetId(long lastIndexedChangeSetId) + { + this.lastIndexedChangeSetId = lastIndexedChangeSetId; + } + + /** + * @return the lastIndexedTxCommitTime + */ + public long getLastIndexedTxCommitTime() + { + return lastIndexedTxCommitTime; + } + + /** + * @param lastIndexedTxCommitTime the lastIndexedTxCommitTime to set + */ + public void setLastIndexedTxCommitTime(long lastIndexedTxCommitTime) + { + this.lastIndexedTxCommitTime = lastIndexedTxCommitTime; + } + + /** + * @return the lastIndexedTxId + */ + public long getLastIndexedTxId() + { + return lastIndexedTxId; + } + + /** + * @param lastIndexedTxId the lastIndexedTxId to set + */ + public void setLastIndexedTxId(long lastIndexedTxId) + { + this.lastIndexedTxId = lastIndexedTxId; + } + + /** + * @return the lastIndexedChangeSetCommitTime + */ + public long getLastIndexedChangeSetCommitTime() + { + return lastIndexedChangeSetCommitTime; + } + + /** + * @param lastIndexedChangeSetCommitTime the lastIndexedChangeSetCommitTime to set + */ + public void setLastIndexedChangeSetCommitTime(long lastIndexedChangeSetCommitTime) + { + this.lastIndexedChangeSetCommitTime = lastIndexedChangeSetCommitTime; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return "ShardState [shardInstance=" + + shardInstance + ", isMaster=" + isMaster + ", lastUpdated=" + lastUpdated + ", lastIndexedChangeSetId=" + lastIndexedChangeSetId + ", lastIndexedTxCommitTime=" + + lastIndexedTxCommitTime + ", lastIndexedTxId=" + lastIndexedTxId + ", lastIndexedChangeSetCommitTime=" + lastIndexedChangeSetCommitTime + ", propertyBag=" + + propertyBag + "]"; + } + + + + +} diff --git a/src/main/java/org/alfresco/repo/index/shard/ShardStateBuilder.java b/src/main/java/org/alfresco/repo/index/shard/ShardStateBuilder.java new file mode 100644 index 0000000..66ccb49 --- /dev/null +++ b/src/main/java/org/alfresco/repo/index/shard/ShardStateBuilder.java @@ -0,0 +1,357 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +// CHECKSTYLE:OFF +/** + * Source code generated by Fluent Builders Generator + * Do not modify this file + * See generator home page at: http://code.google.com/p/fluent-builders-generator-eclipse-plugin/ + */ + +package org.alfresco.repo.index.shard; + +import java.util.HashMap; +import java.util.HashSet; +import org.alfresco.service.cmr.repository.StoreRef; + +public class ShardStateBuilder extends ShardStateBuilderBase +{ + public static ShardStateBuilder shardState() + { + return new ShardStateBuilder(); + } + + public ShardStateBuilder() + { + super(new ShardState()); + } + + public ShardState build() + { + return getInstance(); + } +} + +class ShardStateBuilderBase> +{ + private ShardState instance; + + protected ShardStateBuilderBase(ShardState aInstance) + { + instance = aInstance; + } + + protected ShardState getInstance() + { + return instance; + } + + @SuppressWarnings("unchecked") + public GeneratorT withShardInstance(ShardInstance aValue) + { + instance.setShardInstance(aValue); + + return (GeneratorT) this; + } + + public ShardInstanceShardInstanceBuilder withShardInstance() + { + ShardInstance obj = new ShardInstance(); + + withShardInstance(obj); + + return new ShardInstanceShardInstanceBuilder(obj); + } + + @SuppressWarnings("unchecked") + public GeneratorT withMaster(boolean aValue) + { + instance.setMaster(aValue); + + return (GeneratorT) this; + } + + @SuppressWarnings("unchecked") + public GeneratorT withPropertyBag(HashMap aVa