001    // Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    // http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.ioc.internal;
016    
017    import org.apache.tapestry5.ioc.ObjectLocator;
018    import org.apache.tapestry5.ioc.OrderedConfiguration;
019    import org.apache.tapestry5.ioc.def.ContributionDef;
020    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
021    import org.apache.tapestry5.ioc.internal.util.Orderer;
022    
023    import java.util.Map;
024    
025    /**
026     * Wraps a {@link java.util.List} as a {@link org.apache.tapestry5.ioc.OrderedConfiguration}, implementing validation of
027     * values provided to an {@link org.apache.tapestry5.ioc.OrderedConfiguration}.
028     *
029     * @param <T>
030     */
031    public class ValidatingOrderedConfigurationWrapper<T> extends AbstractConfigurationImpl<T> implements
032            OrderedConfiguration<T>
033    {
034        private final TypeCoercerProxy typeCoercer;
035    
036        private final Orderer<T> orderer;
037    
038        private final Class<T> expectedType;
039    
040        private final Map<String, OrderedConfigurationOverride<T>> overrides;
041    
042        private final ContributionDef contribDef;
043    
044        // Used to supply a default ordering constraint when none is supplied.
045        private String priorId;
046    
047        public ValidatingOrderedConfigurationWrapper(Class<T> expectedType, ObjectLocator locator,
048                                                     TypeCoercerProxy typeCoercer, Orderer<T> orderer, Map<String, OrderedConfigurationOverride<T>> overrides,
049                                                     ContributionDef contribDef)
050        {
051            super(expectedType, locator);
052            this.typeCoercer = typeCoercer;
053    
054            this.orderer = orderer;
055            this.overrides = overrides;
056            this.contribDef = contribDef;
057            this.expectedType = expectedType;
058        }
059    
060        public void add(String id, T object, String... constraints)
061        {
062            T coerced = object == null ? null : typeCoercer.coerce(object, expectedType);
063    
064            // https://issues.apache.org/jira/browse/TAP5-1565
065            // Order each added contribution after the previously added contribution
066            // (in the same method) if no other constraint is supplied.
067            if (constraints.length == 0 && priorId != null)
068            {
069                // Ugly: reassigning parameters is yuck.
070                constraints = new String[]{"after:" + priorId};
071            }
072    
073            orderer.add(id, coerced, constraints);
074    
075            priorId = id;
076        }
077    
078        public void override(String id, T object, String... constraints)
079        {
080            assert InternalUtils.isNonBlank(id);
081    
082            T coerced = object == null ? null : typeCoercer.coerce(object, expectedType);
083    
084            OrderedConfigurationOverride<T> existing = overrides.get(id);
085    
086            if (existing != null)
087                throw new IllegalArgumentException(String.format("Contribution '%s' has already been overridden (by %s).",
088                        id, existing.getContribDef()));
089    
090            overrides.put(id, new OrderedConfigurationOverride<T>(orderer, id, coerced, constraints, contribDef));
091        }
092    
093        public void addInstance(String id, Class<? extends T> clazz, String... constraints)
094        {
095            add(id, instantiate(clazz), constraints);
096        }
097    
098        public void overrideInstance(String id, Class<? extends T> clazz, String... constraints)
099        {
100            override(id, instantiate(clazz), constraints);
101        }
102    }