JcrPropertyDefinition findPropertyDefinition( JcrSession session,
                                                  Name primaryTypeName,
                                                  Collection<Name> mixinTypeNames,
                                                  Name propertyName,
                                                  Value[] values,
                                                  boolean skipProtected,
                                                  boolean checkTypeAndConstraints ) {
        boolean setToEmpty = values == null;
        int propertyType = values == null || values.length == 0 ? PropertyType.STRING : values[0].getType();

        /*
         * We use this flag to indicate that there was a definition encountered with the same name.  If
         * a named definition (or definitions - for example the same node type could define a LONG and BOOLEAN
         * version of the same property) is encountered and no match is found for the name, then processing should not
         * proceed.  If processing did proceed, a residual definition might be found and matched.  This would
         * lead to a situation where a node defined a type for a named property, but contained a property with
         * the same name and the wrong type.
         */
        boolean matchedOnName = false;

        // Look for a multi-value property definition on the primary type that matches by name and type ...
        JcrNodeType primaryType = getNodeType(primaryTypeName);
        if (primaryType != null) {
            for (JcrPropertyDefinition definition : primaryType.allMultiValuePropertyDefinitions(propertyName)) {
                matchedOnName = true;
                // See if the definition allows the value ...
                if (skipProtected && definition.isProtected()) return null;
                if (setToEmpty) {
                    if (!definition.isMandatory()) return definition;
                    // Otherwise this definition doesn't work, so continue with the next ...
                    continue;
                }
                assert values != null;
                // We can use the definition if it matches the type and satisfies the constraints ...
                int type = definition.getRequiredType();
                boolean typeMatches = values.length == 0 || type == PropertyType.UNDEFINED || type == propertyType;
                // Don't check constraints on reference properties
                if (typeMatches) {
                    if (type == PropertyType.REFERENCE) return definition;
                    if (type == PropertyType.WEAKREFERENCE) return definition;
                    if (type == org.modeshape.jcr.api.PropertyType.SIMPLE_REFERENCE) return definition;
                    if (!checkTypeAndConstraints) return definition;
                    if (definition.satisfiesConstraints(values, session)) return definition;
                }
            }

            if (matchedOnName) {
                if (values != null && values.length != 0) {
                    // Nothing was found with matching name and type, so look for definitions with
                    // matching name and an undefined or castable type ...

                    // Look for a multi-value property definition on the primary type that matches by name and type ...
                    for (JcrPropertyDefinition definition : primaryType.allMultiValuePropertyDefinitions(propertyName)) {
                        // See if the definition allows the value ...
                        if (skipProtected && definition.isProtected()) return null;
                        assert definition.getRequiredType() != PropertyType.UNDEFINED;
                        // Don't check constraints on reference properties
                        if (definition.getRequiredType() == PropertyType.REFERENCE && definition.canCastToType(values)) return definition;
                        if (definition.getRequiredType() == PropertyType.WEAKREFERENCE && definition.canCastToType(values)) return definition;
                        if (definition.getRequiredType() == org.modeshape.jcr.api.PropertyType.SIMPLE_REFERENCE
                            && definition.canCastToType(values)) return definition;
                        if (!checkTypeAndConstraints) return definition;
                        if (definition.canCastToTypeAndSatisfyConstraints(values, session)) return definition;
                    }
                }

                return null;
            }
        }

        // Look for a multi-value property definition on the mixin types that matches by name and type ...
        List<JcrNodeType> mixinTypes = null;
        if (mixinTypeNames != null) {
            mixinTypes = new LinkedList<JcrNodeType>();
            for (Name mixinTypeName : mixinTypeNames) {
                JcrNodeType mixinType = getNodeType(mixinTypeName);
                if (mixinType == null) continue;
                mixinTypes.add(mixinType);
                for (JcrPropertyDefinition definition : mixinType.allMultiValuePropertyDefinitions(propertyName)) {
                    matchedOnName = true;
                    // See if the definition allows the value ...
                    if (skipProtected && definition.isProtected()) return null;
                    if (setToEmpty) {
                        if (!definition.isMandatory()) return definition;
                        // Otherwise this definition doesn't work, so continue with the next ...
                        continue;
                    }
                    assert values != null;
                    // We can use the definition if it matches the type and satisfies the constraints ...
                    int type = definition.getRequiredType();
                    boolean typeMatches = values.length == 0 || type == PropertyType.UNDEFINED || type == propertyType;
                    // Don't check constraints on reference properties
                    if (typeMatches) {
                        if (type == PropertyType.REFERENCE) return definition;
                        if (type == PropertyType.WEAKREFERENCE) return definition;
                        if (type == org.modeshape.jcr.api.PropertyType.SIMPLE_REFERENCE) return definition;
                        if (!checkTypeAndConstraints) return definition;
                        if (definition.satisfiesConstraints(values, session)) return definition;
                    }
                }
                if (matchedOnName) {
                    if (values != null && values.length != 0) {
                        // Nothing was found with matching name and type, so look for definitions with
                        // matching name and an undefined or castable type ...

                        // Look for a multi-value property definition on the mixin type that matches by name and type ...
                        for (JcrPropertyDefinition definition : mixinType.allMultiValuePropertyDefinitions(propertyName)) {
                            // See if the definition allows the value ...
                            if (skipProtected && definition.isProtected()) return null;
                            assert definition.getRequiredType() != PropertyType.UNDEFINED;
                            // Don't check constraints on reference properties
                            if (definition.getRequiredType() == PropertyType.REFERENCE && definition.canCastToType(values)) return definition;
                            if (definition.getRequiredType() == PropertyType.WEAKREFERENCE && definition.canCastToType(values)) return definition;
                            if (definition.getRequiredType() == org.modeshape.jcr.api.PropertyType.SIMPLE_REFERENCE
                                && definition.canCastToType(values)) return definition;
                            if (!checkTypeAndConstraints) return definition;
                            if (definition.canCastToTypeAndSatisfyConstraints(values, session)) return definition;
                        }
                    }

                    return null;
                }

            }
        }

        // Nothing was found, so look for residual property definitions ...
        if (!propertyName.equals(JcrNodeType.RESIDUAL_NAME)) return findPropertyDefinition(session, primaryTypeName,
                                                                                           mixinTypeNames,
                                                                                           JcrNodeType.RESIDUAL_NAME, values,
                                                                                           skipProtected, checkTypeAndConstraints);
        return null;
    }
JcrPropertyDefinition findPropertyDefinition ( JcrSession session , Name primaryTypeName , Collection < Name > mixinTypeNames , Name propertyName , Value [ ] values , boolean skipProtected , boolean checkTypeAndConstraints ) { boolean setToEmpty = values == null ; int propertyType = values == null || values . length == 0 ? PropertyType . STRING : values [ 0 ] . getType ( ) ; boolean matchedOnName = false ; JcrNodeType primaryType = getNodeType ( primaryTypeName ) ; if ( primaryType != null ) { for ( JcrPropertyDefinition definition : primaryType . allMultiValuePropertyDefinitions ( propertyName ) ) { matchedOnName = true ; if ( skipProtected && definition . isProtected ( ) ) return null ; if ( setToEmpty ) { if ( ! definition . isMandatory ( ) ) return definition ; continue ; } assert values != null ; int type = definition . getRequiredType ( ) ; boolean typeMatches = values . length == 0 || type == PropertyType . UNDEFINED || type == propertyType ; if ( typeMatches ) { if ( type == PropertyType . REFERENCE ) return definition ; if ( type == PropertyType . WEAKREFERENCE ) return definition ; if ( type == org . modeshape . jcr . api . PropertyType . SIMPLE_REFERENCE ) return definition ; if ( ! checkTypeAndConstraints ) return definition ; if ( definition . satisfiesConstraints ( values , session ) ) return definition ; } } if ( matchedOnName ) { if ( values != null && values . length != 0 ) { for ( JcrPropertyDefinition definition : primaryType . allMultiValuePropertyDefinitions ( propertyName ) ) { if ( skipProtected && definition . isProtected ( ) ) return null ; assert definition . getRequiredType ( ) != PropertyType . UNDEFINED ; if ( definition . getRequiredType ( ) == PropertyType . REFERENCE && definition . canCastToType ( values ) ) return definition ; if ( definition . getRequiredType ( ) == PropertyType . WEAKREFERENCE && definition . canCastToType ( values ) ) return definition ; if ( definition . getRequiredType ( ) == org . modeshape . jcr . api . PropertyType . SIMPLE_REFERENCE && definition . canCastToType ( values ) ) return definition ; if ( ! checkTypeAndConstraints ) return definition ; if ( definition . canCastToTypeAndSatisfyConstraints ( values , session ) ) return definition ; } } return null ; } } List < JcrNodeType > mixinTypes = null ; if ( mixinTypeNames != null ) { mixinTypes = new LinkedList < JcrNodeType > ( ) ; for ( Name mixinTypeName : mixinTypeNames ) { JcrNodeType mixinType = getNodeType ( mixinTypeName ) ; if ( mixinType == null ) continue ; mixinTypes . add ( mixinType ) ; for ( JcrPropertyDefinition definition : mixinType . allMultiValuePropertyDefinitions ( propertyName ) ) { matchedOnName = true ; if ( skipProtected && definition . isProtected ( ) ) return null ; if ( setToEmpty ) { if ( ! definition . isMandatory ( ) ) return definition ; continue ; } assert values != null ; int type = definition . getRequiredType ( ) ; boolean typeMatches = values . length == 0 || type == PropertyType . UNDEFINED || type == propertyType ; if ( typeMatches ) { if ( type == PropertyType . REFERENCE ) return definition ; if ( type == PropertyType . WEAKREFERENCE ) return definition ; if ( type == org . modeshape . jcr . api . PropertyType . SIMPLE_REFERENCE ) return definition ; if ( ! checkTypeAndConstraints ) return definition ; if ( definition . satisfiesConstraints ( values , session ) ) return definition ; } } if ( matchedOnName ) { if ( values != null && values . length != 0 ) { for ( JcrPropertyDefinition definition : mixinType . allMultiValuePropertyDefinitions ( propertyName ) ) { if ( skipProtected && definition . isProtected ( ) ) return null ; assert definition . getRequiredType ( ) != PropertyType . UNDEFINED ; if ( definition . getRequiredType ( ) == PropertyType . REFERENCE && definition . canCastToType ( values ) ) return definition ; if ( definition . getRequiredType ( ) == PropertyType . WEAKREFERENCE && definition . canCastToType ( values ) ) return definition ; if ( definition . getRequiredType ( ) == org . modeshape . jcr . api . PropertyType . SIMPLE_REFERENCE && definition . canCastToType ( values ) ) return definition ; if ( ! checkTypeAndConstraints ) return definition ; if ( definition . canCastToTypeAndSatisfyConstraints ( values , session ) ) return definition ; } } return null ; } } } if ( ! propertyName . equals ( JcrNodeType . RESIDUAL_NAME ) ) return findPropertyDefinition ( session , primaryTypeName , mixinTypeNames , JcrNodeType . RESIDUAL_NAME , values , skipProtected , checkTypeAndConstraints ) ; return null ; }
Searches the supplied primary node type and the mixin node types for a property definition that is the best match for the
given property name, property type, and value.
<p>
This method first attempts to find a single-valued property definition with the supplied property name and
{@link Value#getType() value's property type} in the primary type, skipping any property definitions that are protected.
The property definition is returned if it has a matching type (or has an {@link PropertyType#UNDEFINED undefined property
type}) and the value satisfies the {@link PropertyDefinition#getValueConstraints() definition's constraints}. Otherwise,
the process continues with each of the mixin types, in the order they are named.
</p>
<p>
If no matching property definition could be found (and <code>checkMultiValuedDefinitions</code> parameter is
<code>true</code>), the process is repeated except with multi-valued property definitions with the same name, property
type, and compatible constraints, starting with the primary type and continuing with each mixin type.
</p>
<p>
If no matching property definition could be found, and the process repeats by searching the primary type (and then mixin
types) for single-valued property definitions with a compatible type, where the values can be safely cast to the
definition's property type and still satisfy the definition's constraints.
</p>
<p>
If no matching property definition could be found, the previous step is repeated with multi-valued property definitions.
</p>
<p>
If no matching property definition could be found (and the supplied property name is not the residual name), the whole
process repeats for residual property definitions (e.g., those that are defined with a {@link JcrNodeType#RESIDUAL_NAME "*"
name}).
</p>
<p>
Finally, if no satisfactory property definition could be found, this method returns null.
</p>

@param session the session in which the constraints are to be checked; may not be null
@param primaryTypeName the name of the primary type; may not be null
@param mixinTypeNames the names of the mixin types; may be null or empty if there are no mixins to include in the search
@param propertyName the name of the property for which the definition should be retrieved. This method will automatically
look for residual definitions, but you can use {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve only the best
residual property definition (if any).
@param values the values
@param skipProtected true if this operation is being done from within the public JCR node and property API, or false if
this operation is being done from within internal implementations
@param checkTypeAndConstraints true if the type and constraints of the property definition should be checked, or false
otherwise
@return the best property definition, or <code>null</code> if no property definition allows the property with the supplied
name, type and number of values