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 }