View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.context;
21  
22  import org.slf4j.Logger;
23  import org.slf4j.LoggerFactory;
24  
25  import java.io.Serializable;
26  import java.lang.invoke.MethodHandles;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.Comparator;
30  import java.util.List;
31  
32  public class ThemeImpl implements Theme, Serializable {
33  
34    private static final long serialVersionUID = 1L;
35  
36    private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
37  
38    private String name;
39    private String displayName;
40    private ThemeImpl fallback;
41    private String fallbackName;
42    private List<Theme> fallbackList;
43    private ThemeResources productionResources;
44    private ThemeResources developmentResources;
45    private ThemeScript[] productionScripts;
46    private ThemeStyle[] productionStyles;
47    private ThemeScript[] scripts;
48    private ThemeStyle[] styles;
49    private String version;
50  
51    private boolean locked = false;
52  
53    public ThemeImpl() {
54      developmentResources = new ThemeResources();
55      productionResources = new ThemeResources();
56    }
57  
58    public static ThemeImpl merge(ThemeImpl base, ThemeImpl add) {
59      base.checkUnlocked();
60      add.checkUnlocked();
61      final ThemeImpl result = new ThemeImpl();
62      assert add.name.equals(base.name);
63      result.name = add.name;
64      result.displayName = add.displayName != null ? add.displayName : base.displayName;
65      result.fallbackName = add.fallbackName != null ? add.fallbackName : base.fallbackName;
66      result.version = add.version != null ? add.version : base.version;
67      result.productionResources = ThemeResources.merge(base.productionResources, add.productionResources);
68      result.developmentResources = ThemeResources.merge(base.developmentResources, add.developmentResources);
69      return result;
70    }
71  
72    private void checkUnlocked() throws IllegalStateException {
73      if (locked) {
74        throw new IllegalStateException("The configuration must not be changed after initialization!");
75      }
76    }
77  
78    /**
79     * Lock the configuration, so it cannot be modified any more.
80     */
81    public void lock() {
82      locked = true;
83    }
84  
85    @Override
86    public String getName() {
87      return name;
88    }
89  
90    public void setName(final String name) {
91      checkUnlocked();
92      this.name = name;
93    }
94  
95    @Override
96    public String getDisplayName() {
97      return displayName;
98    }
99  
100   public void setDisplayName(final String displayName) {
101     checkUnlocked();
102     this.displayName = displayName;
103   }
104 
105   public ThemeImpl getFallback() {
106     return fallback;
107   }
108 
109   public void setFallback(final ThemeImpl fallback) {
110     checkUnlocked();
111     this.fallback = fallback;
112   }
113 
114   public String getFallbackName() {
115     return fallbackName;
116   }
117 
118   public void setFallbackName(final String fallbackName) {
119     checkUnlocked();
120     this.fallbackName = fallbackName;
121   }
122 
123   @Override
124   public List<Theme> getFallbackList() {
125     return fallbackList;
126   }
127 
128   public void resolveFallbacks() {
129     checkUnlocked();
130     fallbackList = new ArrayList<>();
131     ThemeImpl actual = this;
132     while (actual != null) {
133       fallbackList.add(actual);
134       actual = actual.getFallback();
135     }
136     fallbackList = Collections.unmodifiableList(fallbackList);
137     if (LOG.isDebugEnabled()) {
138       LOG.debug("fallbackList: {");
139       for (final Theme theme : fallbackList) {
140         LOG.debug("  theme: {}", theme.getName());
141       }
142       LOG.debug("}");
143     }
144   }
145 
146   public void resolveResources() {
147     checkUnlocked();
148     final ThemeImpl fallbackTheme = getFallback();
149     if (fallbackTheme != null) {
150       fallbackTheme.resolveResources();
151       productionResources = ThemeResources.merge(fallbackTheme.getProductionResources(), productionResources);
152       developmentResources = ThemeResources.merge(fallbackTheme.getDevelopmentResources(), developmentResources);
153     }
154   }
155 
156   /**
157    * @deprecated since 5.0.0
158    */
159   @Deprecated
160   public ThemeResources getResources() {
161     return developmentResources;
162   }
163 
164   /**
165    * @since 5.0.0
166    */
167   public ThemeResources getDevelopmentResources() {
168     return developmentResources;
169   }
170 
171   public ThemeResources getProductionResources() {
172     return productionResources;
173   }
174 
175   public void init() {
176     checkUnlocked();
177     productionScripts = sortScripts(productionResources.getScriptList());
178     productionStyles = sortStyles(productionResources.getStyleList());
179     scripts = sortScripts(developmentResources.getScriptList());
180     styles = sortStyles(developmentResources.getStyleList());
181   }
182 
183   private ThemeScript[] sortScripts(List<ThemeScript> list) {
184     final List<ThemeScript> copy = new ArrayList<>(list);
185     copy.sort(Comparator.comparingInt(ThemeResource::getPriority));
186     return copy.toArray(new ThemeScript[0]);
187   }
188 
189   private ThemeStyle[] sortStyles(List<ThemeStyle> list) {
190     final List<ThemeStyle> copy = new ArrayList<>(list);
191     copy.sort(Comparator.comparingInt(ThemeResource::getPriority));
192     return copy.toArray(new ThemeStyle[0]);
193   }
194 
195   @Override
196   public ThemeScript[] getScriptResources(final boolean production) {
197     if (production) {
198       return productionScripts;
199     } else {
200       return scripts;
201     }
202   }
203 
204   @Override
205   public ThemeStyle[] getStyleResources(final boolean production) {
206     if (production) {
207       return productionStyles;
208     } else {
209       return styles;
210     }
211   }
212 
213   @Override
214   public String getVersion() {
215     return version;
216   }
217 
218   public void setVersion(final String version) {
219     checkUnlocked();
220     this.version = version;
221   }
222 
223   public String toString() {
224     final StringBuilder builder = new StringBuilder();
225     builder.append("Theme:  name='");
226     builder.append(name);
227     builder.append("' fallback=");
228     if (fallback != null) {
229       builder.append("'");
230       builder.append(fallback.getName());
231       builder.append("'");
232     } else {
233       builder.append("null");
234     }
235     builder.append(" version='");
236     builder.append(version);
237     builder.append("', \nproductionScripts=[");
238     for (final ThemeScript s : productionScripts) {
239       builder.append("\n");
240       builder.append(s);
241     }
242     builder.append("], \nscripts=[");
243     for (final ThemeScript s : scripts) {
244       builder.append("\n");
245       builder.append(s);
246     }
247     builder.append("], \nproductionStyles=[");
248     for (final ThemeStyle s : productionStyles) {
249       builder.append("\n");
250       builder.append(s);
251     }
252     builder.append("], \nstyles=[");
253     for (final ThemeStyle s : styles) {
254       builder.append("\n");
255       builder.append(s);
256     }
257     return builder.toString();
258   }
259 }