    001    // Copyright 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.services.javascript;
    016
    017    import java.util.Collections;
    018    import java.util.List;
    019
    020    import org.apache.tapestry5.Asset;
    021    import org.apache.tapestry5.func.F;
    022    import org.apache.tapestry5.func.Flow;
    023    import org.apache.tapestry5.func.Mapper;
    024    import org.apache.tapestry5.func.Predicate;
    025    import org.apache.tapestry5.ioc.ServiceBinder;
    026    import org.apache.tapestry5.ioc.ServiceBindingOptions;
    027    import org.apache.tapestry5.ioc.annotations.UsesOrderedConfiguration;
    028    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    029    import org.apache.tapestry5.services.AssetSource;
    030
    031    /**
    032     * An extensible implementation of {@link JavaScriptStack} that can be used as the implementation of a service.
    033     * The contributions to the service are used to supply the libraries, stylesheets, and initialization for a
    034     * JavaScriptStack, allowing the stack to be more dynamically configured. In practice, one will use
    035     * {@link ServiceBinder#bind(Class, Class)} and {@link ServiceBindingOptions#withMarker(Class...)} to construct the
    036     * service, then use the marker annotation to inject the service when contributing the service into to the
    037     * {@link JavaScriptStackSource}.
    038     * <p>
    039     * A limitation of this implementation is that the contributed assets are not localized at all.
    040     *
    041     * @since 5.3
    042     * @see StackExtension
    043     */
    044    @UsesOrderedConfiguration(StackExtension.class)
    045    public class ExtensibleJavaScriptStack implements JavaScriptStack
    046    {
    047        private final AssetSource assetSource;
    048
    049        private final List<Asset> libraries;
    050
    051        private final List<StylesheetLink> stylesheets;
    052
    053        private final String initialization;
    054
    055        private final Predicate<StackExtension> by(final StackExtensionType type)
    056        {
    057            return new Predicate<StackExtension>()
    058            {
    059                public boolean accept(StackExtension element)
    060                {
    061                    return element.type == type;
    062                }
    063            };
    064        }
    065
    066        private final Mapper<StackExtension, String> extractValue = new Mapper<StackExtension, String>()
    067        {
    068            public String map(StackExtension element)
    069            {
    070                return element.value;
    071            };
    072        };
    073
    074        private final Mapper<String, Asset> stringToAsset = new Mapper<String, Asset>()
    075        {
    076            public Asset map(String value)
    077            {
    078                return assetSource.getExpandedAsset(value);
    079            };
    080        };
    081
    082        private final Mapper<Asset, StylesheetLink> assetToStylesheetLink = new Mapper<Asset, StylesheetLink>()
    083        {
    084            public StylesheetLink map(Asset asset)
    085            {
    086                return new StylesheetLink(asset);
    087            };
    088        };
    089
    090        public ExtensibleJavaScriptStack(AssetSource assetSource, List<StackExtension> configuration)
    091        {
    092            this.assetSource = assetSource;
    093
    094            Flow<StackExtension> extensions = F.flow(configuration);
    095
    096            libraries = extensions.filter(by(StackExtensionType.LIBRARY)).map(extractValue).map(stringToAsset).toList();
    097
    098            stylesheets = extensions.filter(by(StackExtensionType.STYLESHEET)).map(extractValue).map(stringToAsset)
    099                    .map(assetToStylesheetLink).toList();
    100
    101            List<String> initializations = extensions.filter(by(StackExtensionType.INITIALIZATION)).map(extractValue)
    102                    .toList();
    103
    104            initialization = initializations.isEmpty() ? null : InternalUtils.join(initializations, "\n");
    105        }
    106
    107        public List<String> getStacks()
    108        {
    109            return Collections.emptyList();
    110        }
    111
    112        public List<Asset> getJavaScriptLibraries()
    113        {
    114            return libraries;
    115        }
    116
    117        public List<StylesheetLink> getStylesheets()
    118        {
    119            return stylesheets;
    120        }
    121
    122        public String getInitialization()
    123        {
    124            return initialization;
    125        }
    126
    127    }