final AbstractJcrProperty setProperty( Name name,
                                           Value[] values,
                                           int jcrPropertyType,
                                           boolean skipProtectedValidation,
                                           boolean skipReferenceValidation,
                                           boolean skipPermissionsCheck,
                                           boolean skipVersioningValidation )
        throws VersionException, LockException, ConstraintViolationException, RepositoryException {
        assert values != null;
        checkForLock();
        if (!skipVersioningValidation) {
            checkForCheckedOut();
        }
        checkNodeTypeCanBeModified();

        if (!skipPermissionsCheck) {
            session.checkPermission(this, ModeShapePermissions.SET_PROPERTY);
        }

        values = compactValues(values);
        checkAllValuesHaveTheSameType(values, name);

        // Force a conversion to the specified property type (if required) ...
        if (jcrPropertyType != PropertyType.UNDEFINED) {
            int len = values.length;
            JcrValue[] newValues = null;
            if (len == 0) {
                newValues = JcrMultiValueProperty.EMPTY_VALUES;
            } else {
                List<JcrValue> valuesWithDesiredType = new ArrayList<JcrValue>(len);
                for (int i = 0; i != len; ++i) {
                    JcrValue value = (JcrValue)values[i];
                    value = value.asType(jcrPropertyType);
                    valuesWithDesiredType.add(value);
                }
                if (valuesWithDesiredType.isEmpty()) {
                    newValues = JcrMultiValueProperty.EMPTY_VALUES;
                } else {
                    newValues = valuesWithDesiredType.toArray(new JcrValue[valuesWithDesiredType.size()]);
                }
            }
            values = newValues;
        }

        // Check for an existing JCR property object; note that this will load the internal property if necessary ...
        AbstractJcrProperty existing = getProperty(name);
        if (existing != null) {
            // Found an existing property, and per the JavaDoc for the multi-valued javax.jcr.Node#setProperty(...),
            // this method is to throw an exception if there is an existing property and it is not already multi-valued ...
            if (!existing.isMultiple()) {
                // The cardinality of the new values does not match the cardinality of the existing property ...
                I18n msg = JcrI18n.unableToSetSingleValuedPropertyUsingMultipleValues;
                throw new javax.jcr.ValueFormatException(msg.text(readable(name), location(), workspaceName()));
            }
            if (existing.getDefinition().getRequiredType() == jcrPropertyType) {
                try {
                    // set the property via the public method, so that additional checks are performed
                    existing.setValue(values);
                } catch (VersionException e) {
                    if (skipVersioningValidation) {
                        // the node is checked in, but we should ignore that, so set the property via the protected method
                        ((JcrMultiValueProperty)existing).internalSetValue(values);
                    } else {
                        throw e;
                    }
                }
                return existing;
            }
        }

        // Otherwise, we have to create the property, so first find a valid property definition ...
        SessionCache cache = sessionCache();
        MutableCachedNode node = mutable();
        Name primaryType = node.getPrimaryType(cache);
        Set<Name> mixinTypes = node.getMixinTypes(cache);
        NodeTypes nodeTypes = session.nodeTypes();
        JcrPropertyDefinition defn = null;
        defn = nodeTypes.findPropertyDefinition(session, primaryType, mixinTypes, name, values, !skipProtectedValidation,
                                                skipReferenceValidation);

        if (defn == null) {
            // Failed to find a valid property definition,
            // so figure out if there's a definition that would work if it had no constraints ...
            defn = nodeTypes.findPropertyDefinition(session, primaryType, mixinTypes, name, values, !skipProtectedValidation,
                                                    false);

            String propName = readable(name);
            if (defn != null) {
                String defnName = defn.getName();
                String nodeTypeName = defn.getDeclaringNodeType().getName();
                I18n msg = JcrI18n.valueViolatesConstraintsOnDefinition;
                throw new ConstraintViolationException(msg.text(propName, readable(values), location(), defnName, nodeTypeName));
            }
            // See if we can find a single-valued property definition ...
            defn = nodeTypes.findPropertyDefinition(session, primaryType, mixinTypes, name, values[0], true, false);
            if (defn == null) {
                // The cardinality of the new values does not match the available property definition ...
                I18n msg = JcrI18n.unableToSetSingleValuedPropertyUsingMultipleValues;
                throw new javax.jcr.ValueFormatException(msg.text(readable(name), location(), workspaceName()));
            }
            I18n msg = JcrI18n.noPropertyDefinition;
            throw new ConstraintViolationException(msg.text(propName, location(), readable(primaryType), readable(mixinTypes)));
        }

        // The 'findBestPropertyDefintion' method checks constraints for all definitions exception those with a
        // require type of REFERENCE. This is because checking such constraints may cause unnecessary loading of nodes.
        // Therefore, see if this is the case ...
        int requiredType = defn.getRequiredType();
        if (!skipReferenceValidation && (requiredType == PropertyType.REFERENCE || requiredType == PropertyType.WEAKREFERENCE)) {
            // Check that the REFERENCE value satisfies the constraints ...
            if (!defn.canCastToTypeAndSatisfyConstraints(values, session)) {
                // The REFERENCE value did not satisfy the constraints ...
                String propName = readable(name);
                String defnName = defn.getName();
                String nodeTypeName = defn.getDeclaringNodeType().getName();
                I18n i18n = JcrI18n.weakReferenceValueViolatesConstraintsOnDefinition;
                if (requiredType == PropertyType.REFERENCE) i18n = JcrI18n.referenceValueViolatesConstraintsOnDefinition;
                throw new ConstraintViolationException(i18n.text(propName, readable(values), location(), defnName, nodeTypeName));
            }
        }

        if (requiredType == PropertyType.UNDEFINED && values.length > 0) {
            requiredType = values[0].getType();
        }

        // Create the JCR Property object ...
        AbstractJcrProperty jcrProp = new JcrMultiValueProperty(this, name, requiredType);
        jcrProp.setPropertyDefinitionId(defn.getId(), nodeTypes.getVersion());
        AbstractJcrProperty otherProp = this.jcrProperties.putIfAbsent(name, jcrProp);
        if (otherProp != null) {
            // Someone snuck in and created this property while we created ours, so use that instance instead ...
            // Check that the cardinality is correct ...
            if (!jcrProp.isMultiple()) {
                // Overwrite the value anyway ...
                this.jcrProperties.put(name, jcrProp);
            }
            jcrProp = otherProp;
        }

        // The values may need to be converted to the definition's required type ...
        int numValues = values.length;
        Object[] objValues = new Object[numValues];
        int propertyType = defn.getRequiredType();
        if (propertyType == PropertyType.UNDEFINED || propertyType == jcrPropertyType) {
            // Can use the values as is ...
            for (int i = 0; i != numValues; ++i) {
                objValues[i] = ((JcrValue)values[i]).value();
            }
        } else {
            // A conversion is required ...
            try {
                org.modeshape.jcr.value.PropertyType msType = PropertyTypeUtil.modePropertyTypeFor(propertyType);
                org.modeshape.jcr.value.ValueFactory<?> factory = context().getValueFactories().getValueFactory(msType);
                for (int i = 0; i != numValues; ++i) {
                    objValues[i] = factory.create(((JcrValue)values[i]).value());
                }
            } catch (org.modeshape.jcr.value.ValueFormatException e) {
                throw new ValueFormatException(e.getMessage());
            }
        }

        // Set the property on the cached node (even if there was an 'otherProp' instance) ...
        Property newProperty = session.propertyFactory().create(name, objValues);
        node.setProperty(cache, newProperty);
        return jcrProp;
    }
final AbstractJcrProperty setProperty ( Name name , Value [ ] values , int jcrPropertyType , boolean skipProtectedValidation , boolean skipReferenceValidation , boolean skipPermissionsCheck , boolean skipVersioningValidation ) throws VersionException , LockException , ConstraintViolationException , RepositoryException { assert values != null ; checkForLock ( ) ; if ( ! skipVersioningValidation ) { checkForCheckedOut ( ) ; } checkNodeTypeCanBeModified ( ) ; if ( ! skipPermissionsCheck ) { session . checkPermission ( this , ModeShapePermissions . SET_PROPERTY ) ; } values = compactValues ( values ) ; checkAllValuesHaveTheSameType ( values , name ) ; if ( jcrPropertyType != PropertyType . UNDEFINED ) { int len = values . length ; JcrValue [ ] newValues = null ; if ( len == 0 ) { newValues = JcrMultiValueProperty . EMPTY_VALUES ; } else { List < JcrValue > valuesWithDesiredType = new ArrayList < JcrValue > ( len ) ; for ( int i = 0 ; i != len ; ++ i ) { JcrValue value = ( JcrValue ) values [ i ] ; value = value . asType ( jcrPropertyType ) ; valuesWithDesiredType . add ( value ) ; } if ( valuesWithDesiredType . isEmpty ( ) ) { newValues = JcrMultiValueProperty . EMPTY_VALUES ; } else { newValues = valuesWithDesiredType . toArray ( new JcrValue [ valuesWithDesiredType . size ( ) ] ) ; } } values = newValues ; } AbstractJcrProperty existing = getProperty ( name ) ; if ( existing != null ) { if ( ! existing . isMultiple ( ) ) { I18n msg = JcrI18n . unableToSetSingleValuedPropertyUsingMultipleValues ; throw new javax . jcr . ValueFormatException ( msg . text ( readable ( name ) , location ( ) , workspaceName ( ) ) ) ; } if ( existing . getDefinition ( ) . getRequiredType ( ) == jcrPropertyType ) { try { existing . setValue ( values ) ; } catch ( VersionException e ) { if ( skipVersioningValidation ) { ( ( JcrMultiValueProperty ) existing ) . internalSetValue ( values ) ; } else { throw e ; } } return existing ; } } SessionCache cache = sessionCache ( ) ; MutableCachedNode node = mutable ( ) ; Name primaryType = node . getPrimaryType ( cache ) ; Set < Name > mixinTypes = node . getMixinTypes ( cache ) ; NodeTypes nodeTypes = session . nodeTypes ( ) ; JcrPropertyDefinition defn = null ; defn = nodeTypes . findPropertyDefinition ( session , primaryType , mixinTypes , name , values , ! skipProtectedValidation , skipReferenceValidation ) ; if ( defn == null ) { defn = nodeTypes . findPropertyDefinition ( session , primaryType , mixinTypes , name , values , ! skipProtectedValidation , false ) ; String propName = readable ( name ) ; if ( defn != null ) { String defnName = defn . getName ( ) ; String nodeTypeName = defn . getDeclaringNodeType ( ) . getName ( ) ; I18n msg = JcrI18n . valueViolatesConstraintsOnDefinition ; throw new ConstraintViolationException ( msg . text ( propName , readable ( values ) , location ( ) , defnName , nodeTypeName ) ) ; } defn = nodeTypes . findPropertyDefinition ( session , primaryType , mixinTypes , name , values [ 0 ] , true , false ) ; if ( defn == null ) { I18n msg = JcrI18n . unableToSetSingleValuedPropertyUsingMultipleValues ; throw new javax . jcr . ValueFormatException ( msg . text ( readable ( name ) , location ( ) , workspaceName ( ) ) ) ; } I18n msg = JcrI18n . noPropertyDefinition ; throw new ConstraintViolationException ( msg . text ( propName , location ( ) , readable ( primaryType ) , readable ( mixinTypes ) ) ) ; } int requiredType = defn . getRequiredType ( ) ; if ( ! skipReferenceValidation && ( requiredType == PropertyType . REFERENCE || requiredType == PropertyType . WEAKREFERENCE ) ) { if ( ! defn . canCastToTypeAndSatisfyConstraints ( values , session ) ) { String propName = readable ( name ) ; String defnName = defn . getName ( ) ; String nodeTypeName = defn . getDeclaringNodeType ( ) . getName ( ) ; I18n i18n = JcrI18n . weakReferenceValueViolatesConstraintsOnDefinition ; if ( requiredType == PropertyType . REFERENCE ) i18n = JcrI18n . referenceValueViolatesConstraintsOnDefinition ; throw new ConstraintViolationException ( i18n . text ( propName , readable ( values ) , location ( ) , defnName , nodeTypeName ) ) ; } } if ( requiredType == PropertyType . UNDEFINED && values . length > 0 ) { requiredType = values [ 0 ] . getType ( ) ; } AbstractJcrProperty jcrProp = new JcrMultiValueProperty ( this , name , requiredType ) ; jcrProp . setPropertyDefinitionId ( defn . getId ( ) , nodeTypes . getVersion ( ) ) ; AbstractJcrProperty otherProp = this . jcrProperties . putIfAbsent ( name , jcrProp ) ; if ( otherProp != null ) { if ( ! jcrProp . isMultiple ( ) ) { this . jcrProperties . put ( name , jcrProp ) ; } jcrProp = otherProp ; } int numValues = values . length ; Object [ ] objValues = new Object [ numValues ] ; int propertyType = defn . getRequiredType ( ) ; if ( propertyType == PropertyType . UNDEFINED || propertyType == jcrPropertyType ) { for ( int i = 0 ; i != numValues ; ++ i ) { objValues [ i ] = ( ( JcrValue ) values [ i ] ) . value ( ) ; } } else { try { org . modeshape . jcr . value . PropertyType msType = PropertyTypeUtil . modePropertyTypeFor ( propertyType ) ; org . modeshape . jcr . value . ValueFactory < ? > factory = context ( ) . getValueFactories ( ) . getValueFactory ( msType ) ; for ( int i = 0 ; i != numValues ; ++ i ) { objValues [ i ] = factory . create ( ( ( JcrValue ) values [ i ] ) . value ( ) ) ; } } catch ( org . modeshape . jcr . value . ValueFormatException e ) { throw new ValueFormatException ( e . getMessage ( ) ) ; } } Property newProperty = session . propertyFactory ( ) . create ( name , objValues ) ; node . setProperty ( cache , newProperty ) ; return jcrProp ; }
@param name the name of the property; may not be null
@param values the values of the property; may not be null
@param jcrPropertyType the expected property type; may be {@link javax.jcr.PropertyType#UNDEFINED} if the values should not
be converted
@param skipProtectedValidation true if protected properties can be set by the caller of this method, or false if the method
should validate that protected methods are not being called
@param skipReferenceValidation indicates whether constraints on REFERENCE properties should be enforced
@param skipPermissionsCheck true if this method should be executed without permission check.
@param skipVersioningValidation true if the property can be set even if a node is checked in
@return the new JCR property object
@throws VersionException if the node is checked out
@throws LockException if the node is locked
@throws ConstraintViolationException if the new value would violate the constraints on the property definition
@throws RepositoryException if the named property does not exist, or if some other error occurred