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 }