public List match()

in commons-digester3-core/src/main/java/org/apache/commons/digester3/ExtendedBaseRules.java [178:478]


    public List<Rule> match( final String namespaceURI, final String pattern, final String name, final Attributes attributes )
    {
        // calculate the pattern of the parent
        // (if the element has one)
        String parentPattern = "";
        final int lastIndex = pattern.lastIndexOf( '/' );

        boolean hasParent = true;
        if ( lastIndex == -1 )
        {
            // element has no parent
            hasParent = false;
        }
        else
        {
            // calculate the pattern of the parent
            parentPattern = pattern.substring( 0, lastIndex );
        }

        // we keep the list of universal matches separate
        final List<Rule> universalList = new ArrayList<Rule>( counter );

        // Universal wildcards ('*') in the middle of the pattern-string
        List<Rule> recList = null;
        // temporary parentPattern
        // we don't want to change anything....
        String tempParentPattern = parentPattern;
        int parentLastIndex = tempParentPattern.lastIndexOf( '/' );
        // look for pattern. Here, we search the whole
        // parent. Not ideal, but does the thing....
        while ( parentLastIndex > -1 && recList == null )
        {
            recList = this.cache.get( tempParentPattern + "/*/" + pattern.substring( lastIndex + 1 ) );
            if ( recList != null )
            {
                // when /*/-pattern-string is found, add method
                // list to universalList.
                // Digester will do the rest
                universalList.addAll( recList );
            }
            else
            {
                // if not, shorten tempParent to move /*/ one position
                // to the left.
                // as last part of patttern is always added
                // we make sure pattern is allowed anywhere.
                tempParentPattern = parentPattern.substring( 0, parentLastIndex );
            }

            parentLastIndex = tempParentPattern.lastIndexOf( '/' );
        }

        // Universal all wildards ('!*')
        // These are always matched so always add them
        List<Rule> tempList = this.cache.get( "!*" );
        if ( tempList != null )
        {
            universalList.addAll( tempList );
        }

        // Universal exact parent match
        // need to get this now since only wildcards are considered later
        tempList = this.cache.get( "!" + parentPattern + "/?" );
        if ( tempList != null )
        {
            universalList.addAll( tempList );
        }

        // base behavior means that if we certain matches, we don't continue
        // but we just have a single combined loop and so we have to set
        // a variable
        boolean ignoreBasicMatches = false;

        // see if we have an exact basic pattern match
        List<Rule> rulesList = this.cache.get( pattern );
        if ( rulesList != null )
        {
            // we have a match!
            // so ignore all basic matches from now on
            ignoreBasicMatches = true;

        }
        else
        {

            // see if we have an exact child match
            if ( hasParent )
            {
                // matching children takes preference
                rulesList = this.cache.get( parentPattern + "/?" );
                if ( rulesList != null )
                {
                    // we have a match!
                    // so ignore all basic matches from now on
                    ignoreBasicMatches = true;

                }
                else
                {
                    // we don't have a match yet - so try exact ancester
                    //
                    rulesList = findExactAncesterMatch( pattern );
                    if ( rulesList != null )
                    {
                        // we have a match!
                        // so ignore all basic matches from now on
                        ignoreBasicMatches = true;
                    }
                }
            }
        }

        // OK - we're ready for the big loop!
        // Unlike the basic rules case,
        // we have to go through for all those universal rules in all cases.

        // Find the longest key, ie more discriminant
        int longKeyLength = 0;

        for ( String key : this.cache.keySet() )
        {

            // find out if it's a univeral pattern
            // set a flag
            final boolean isUniversal = key.startsWith( "!" );
            if ( isUniversal )
            {
                // and find the underlying key
                key = key.substring( 1 );
            }

            // don't need to check exact matches
            final boolean wildcardMatchStart = key.startsWith( "*/" );
            final boolean wildcardMatchEnd = key.endsWith( "/*" );
            if ( wildcardMatchStart || ( isUniversal && wildcardMatchEnd ) )
            {

                boolean parentMatched = false;
                boolean basicMatched = false;
                boolean ancesterMatched = false;

                final boolean parentMatchEnd = key.endsWith( "/?" );
                if ( parentMatchEnd )
                {
                    // try for a parent match
                    parentMatched = parentMatch( key, parentPattern );

                }
                else if ( wildcardMatchEnd )
                {
                    // check for ancester match
                    if ( wildcardMatchStart )
                    {
                        final String patternBody = key.substring( 2, key.length() - 2 );
                        if ( pattern.endsWith( patternBody ) )
                        {
                            ancesterMatched = true;
                        }
                        else
                        {
                            ancesterMatched = ( pattern.indexOf( patternBody + "/" ) > -1 );
                        }
                    }
                    else
                    {
                        final String bodyPattern = key.substring( 0, key.length() - 2 );
                        if ( pattern.startsWith( bodyPattern ) )
                        {
                            if ( pattern.length() == bodyPattern.length() )
                            {
                                // exact match
                                ancesterMatched = true;
                            }
                            else
                            {
                                ancesterMatched = ( pattern.charAt( bodyPattern.length() ) == '/' );
                            }
                        }
                        else
                        {
                            ancesterMatched = false;
                        }
                    }
                }
                else
                {
                    // try for a base match
                    basicMatched = basicMatch( key, pattern );
                }

                if ( parentMatched || basicMatched || ancesterMatched )
                {
                    if ( isUniversal )
                    {
                        // universal rules go straight in
                        // (no longest matching rule)
                        tempList = this.cache.get( "!" + key );
                        if ( tempList != null )
                        {
                            universalList.addAll( tempList );
                        }

                    }
                    else
                    {
                        if ( !ignoreBasicMatches )
                        {
                            // ensure that all parent matches are SHORTER
                            // than rules with same level of matching.
                            //
                            // the calculations below don't work for universal
                            // matching, but we don't care because in that case
                            // this if-stmt is not entered.
                            int keyLength = key.length();
                            if ( wildcardMatchStart )
                            {
                                --keyLength;
                            }
                            if ( wildcardMatchEnd )
                            {
                                --keyLength;
                            }
                            else if ( parentMatchEnd )
                            {
                                --keyLength;
                            }

                            if ( keyLength > longKeyLength )
                            {
                                rulesList = this.cache.get( key );
                                longKeyLength = keyLength;
                            }
                        }
                    }
                }
            }
        }

        // '*' works in practice as a default matching
        // (this is because anything is a deeper match!)
        if ( rulesList == null )
        {
            rulesList = this.cache.get( "*" );
        }

        // if we've matched a basic pattern, then add to the universal list
        if ( rulesList != null )
        {
            universalList.addAll( rulesList );
        }

        // don't filter if namespace is null
        if ( namespaceURI != null )
        {
            // remove invalid namespaces
            final Iterator<Rule> it = universalList.iterator();
            while ( it.hasNext() )
            {
                final Rule rule = it.next();
                final String nsUri = rule.getNamespaceURI();
                if ( nsUri != null && !nsUri.equals( namespaceURI ) )
                {
                    it.remove();
                }
            }
        }

        // need to make sure that the collection is sort in the order
        // of addition. We use a custom comparator for this
        Collections.sort( universalList, new Comparator<Rule>()
        {

            @Override
            public int compare( final Rule r1, final Rule r2 )
            {
                // Get the entry order from the map
                final Integer i1 = order.get( r1 );
                final Integer i2 = order.get( r2 );

                // and use that to perform the comparison
                if ( i1 == null )
                {
                    if ( i2 == null )
                    {

                        return 0;

                    }
                    return -1;
                }
                if ( i2 == null )
                {
                    return 1;
                }

                return ( i1.intValue() - i2.intValue() );
            }
        } );

        return universalList;
    }