001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  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 distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest.widget;
014
015import java.io.*;
016import java.util.*;
017
018import org.apache.juneau.rest.*;
019import org.apache.juneau.svl.*;
020import org.apache.juneau.utils.*;
021import org.apache.juneau.html.*;
022
023/**
024 * Defines an interface for resolvers of <js>"$W{...}"</js> string variables.
025 *
026 * <p>
027 * Widgets must provide one of the following public constructors:
028 * <ul>
029 *    <li><code><jk>public</jk> Widget();</code>
030 *    <li><code><jk>public</jk> Widget(PropertyStore);</code>
031 * </ul>
032 *
033 * <p>
034 * Widgets can be defined as inner classes of REST resource classes.
035 *
036 * <ul class='seealso'>
037 *    <li class='link'>{@doc juneau-rest-server.HtmlDocAnnotation.Widgets}
038 * </ul>
039 */
040public abstract class Widget implements HtmlWidget {
041
042   private final ClasspathResourceManager rm = new ClasspathResourceManager(getClass(), ClasspathResourceFinderRecursive.INSTANCE, false);
043
044   private static final String SESSION_req = "req";
045   private static final String SESSION_res = "res";
046
047   /**
048    * The widget key.
049    *
050    * <p>
051    * (i.e. The variable name inside the <js>"$W{...}"</js> variable).
052    *
053    * <p>
054    * The returned value must not be <jk>null</jk>.
055    *
056    * <p>
057    * If not overridden, the default value is the class simple name.
058    *
059    * @return The widget key.
060    */
061   @Override
062   public String getName() {
063      return getClass().getSimpleName();
064   }
065
066   private RestRequest req(VarResolverSession session) {
067      return session.getSessionObject(RestRequest.class, SESSION_req, true);
068   }
069
070   private RestResponse res(VarResolverSession session) {
071      return session.getSessionObject(RestResponse.class, SESSION_res, true);
072   }
073
074   @Override /* HtmlWidget */
075   public String getHtml(VarResolverSession session) throws Exception {
076      return getHtml(req(session), res(session));
077   }
078
079   @Override /* HtmlWidget */
080   public String getScript(VarResolverSession session) throws Exception {
081      return getScript(req(session), res(session));
082   }
083
084   @Override /* HtmlWidget */
085   public String getStyle(VarResolverSession session) throws Exception {
086      return getStyle(req(session), res(session));
087   }
088
089   /**
090    * Resolves the HTML content for this widget.
091    *
092    * <p>
093    * A returned value of <jk>null</jk> will cause nothing to be added to the page.
094    *
095    * @param req The HTTP request object.
096    * @param res The current HTTP response.
097    * @return The HTML content of this widget.
098    * @throws Exception Error occurred.
099    */
100   public String getHtml(RestRequest req, RestResponse res) throws Exception {
101      return null;
102   }
103
104   /**
105    * Implement {@link #getHtml(RestRequest, RestResponse)}.
106    */
107   @SuppressWarnings("javadoc")
108   @Deprecated
109   public String getHtml(RestRequest req) throws Exception {
110      return getHtml(req, null);
111   }
112
113   /**
114    * Resolves any Javascript that should be added to the <xt>&lt;head&gt;/&lt;script&gt;</xt> element.
115    *
116    * <p>
117    * A returned value of <jk>null</jk> will cause nothing to be added to the page.
118    *
119    * @param req The HTTP request object.
120    * @param res The current HTTP response.
121    * @return The Javascript needed by this widget.
122    * @throws Exception Error occurred.
123    */
124   public String getScript(RestRequest req, RestResponse res) throws Exception {
125      return null;
126   }
127
128   /**
129    * Implement {@link #getScript(RestRequest, RestResponse)}.
130    */
131   @SuppressWarnings("javadoc")
132   @Deprecated
133   public String getScript(RestRequest req) throws Exception {
134      return getScript(req, null);
135   }
136
137   /**
138    * Resolves any CSS styles that should be added to the <xt>&lt;head&gt;/&lt;style&gt;</xt> element.
139    *
140    * <p>
141    * A returned value of <jk>null</jk> will cause nothing to be added to the page.
142    *
143    * @param req The HTTP request object.
144    * @param res The current HTTP response.
145    * @return The CSS styles needed by this widget.
146    * @throws Exception Error occurred.
147    */
148   public String getStyle(RestRequest req, RestResponse res) throws Exception {
149      return null;
150   }
151
152   /**
153    * Implement {@link #getStyle(RestRequest, RestResponse)}.
154    */
155   @SuppressWarnings("javadoc")
156   @Deprecated
157   public String getStyle(RestRequest req) throws Exception {
158      return getStyle(req, null);
159   }
160
161   /**
162    * Retrieves the specified classpath resource and returns the contents as a string.
163    *
164    * <p>
165    * Same as {@link Class#getResourceAsStream(String)} except if it doesn't find the resource on this class, searches
166    * up the parent hierarchy chain.
167    *
168    * <p>
169    * If the resource cannot be found in the classpath, then an attempt is made to look relative to the JVM working directory.
170    * <br>Path traversals outside the working directory are not allowed for security reasons.
171    *
172    * @param name Name of the desired resource.
173    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
174    * @throws IOException Thrown by underlying stream.
175    */
176   protected String getClasspathResourceAsString(String name) throws IOException {
177      return rm.getString(name);
178   }
179
180   /**
181    * Same as {@link #getClasspathResourceAsString(String)} except also looks for localized-versions of the file.
182    *
183    * <p>
184    * If the <c>locale</c> is specified, then we look for resources whose name matches that locale.
185    * <br>For example, if looking for the resource <js>"MyResource.txt"</js> for the Japanese locale, we will look for
186    * files in the following order:
187    * <ol>
188    *    <li><js>"MyResource_ja_JP.txt"</js>
189    *    <li><js>"MyResource_ja.txt"</js>
190    *    <li><js>"MyResource.txt"</js>
191    * </ol>
192    *
193    * @param name Name of the desired resource.
194    * @param locale The locale.  Can be <jk>null</jk>.
195    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
196    * @throws IOException Thrown by underlying stream.
197    */
198   protected String getClasspathResourceAsString(String name, Locale locale) throws IOException {
199      return rm.getString(name, locale);
200   }
201
202   /**
203    * Convenience method for calling {@link #getClasspathResourceAsString(String)} except also strips Javascript comments from
204    * the file.
205    *
206    * <p>
207    * Comments are assumed to be Java-style block comments: <js>"/*"</js>.
208    *
209    * @param name Name of the desired resource.
210    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
211    * @throws IOException Thrown by underlying stream.
212    */
213   protected String loadScript(String name) throws IOException {
214      String s = getClasspathResourceAsString(name);
215      if (s != null)
216         s = s.replaceAll("(?s)\\/\\*(.*?)\\*\\/\\s*", "");
217      return s;
218   }
219
220   /**
221    * Same as {@link #loadScript(String)} but replaces request-time SVL variables.
222    *
223    * <ul class='seealso'>
224    *    <li class='jm'>{@link org.apache.juneau.rest.RestContext#getVarResolver()}
225    *    <li class='link'>{@doc juneau-rest-server.SvlVariables}
226    * </ul>
227    *
228    * @param req The current HTTP request.
229    * @param res The current HTTP response.
230    * @param name Name of the desired resource.
231    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
232    * @throws IOException Thrown by underlying stream.
233    */
234   protected String loadScriptWithVars(RestRequest req, RestResponse res, String name) throws IOException {
235      return req.getVarResolverSession().resolve(loadScript(name));
236   }
237
238   /**
239    * Convenience method for calling {@link #getClasspathResourceAsString(String)} except also strips CSS comments from
240    * the file.
241    *
242    * <p>
243    * Comments are assumed to be Java-style block comments: <js>"/*"</js>.
244    *
245    * @param name Name of the desired resource.
246    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
247    * @throws IOException Thrown by underlying stream.
248    */
249   protected String loadStyle(String name) throws IOException {
250      String s = getClasspathResourceAsString(name);
251      if (s != null)
252         s = s.replaceAll("(?s)\\/\\*(.*?)\\*\\/\\s*", "");
253      return s;
254   }
255
256   /**
257    * Same as {@link #loadStyle(String)} but replaces request-time SVL variables.
258    *
259    * <ul class='seealso'>
260    *    <li class='jm'>{@link org.apache.juneau.rest.RestContext#getVarResolver()}
261    *    <li class='link'>{@doc juneau-rest-server.SvlVariables}
262    * </ul>
263    *
264    * @param req The current HTTP request.
265    * @param res The current HTTP response.
266    * @param name Name of the desired resource.
267    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
268    * @throws IOException Thrown by underlying stream.
269    */
270   protected String loadStyleWithVars(RestRequest req, RestResponse res, String name) throws IOException {
271      return req.getVarResolverSession().resolve(loadStyle(name));
272   }
273
274   /**
275    * Convenience method for calling {@link #getClasspathResourceAsString(String)} except also strips HTML comments from the
276    * file.
277    *
278    * <p>
279    * Comment are assumed to be <js>"<!-- -->"</js> code blocks.
280    *
281    * @param name Name of the desired resource.
282    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
283    * @throws IOException Thrown by underlying stream.
284    */
285   protected String loadHtml(String name) throws IOException {
286      String s = getClasspathResourceAsString(name);
287      if (s != null)
288         s = s.replaceAll("(?s)<!--(.*?)-->\\s*", "");
289      return s;
290   }
291
292   /**
293    * Same as {@link #loadHtml(String)} but replaces request-time SVL variables.
294    *
295    * <ul class='seealso'>
296    *    <li class='jm'>{@link org.apache.juneau.rest.RestContext#getVarResolver()}
297    *    <li class='link'>{@doc juneau-rest-server.SvlVariables}
298    * </ul>
299    *
300    * @param req The current HTTP request.
301    * @param res The current HTTP response.
302    * @param name Name of the desired resource.
303    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
304    * @throws IOException Thrown by underlying stream.
305    */
306   protected String loadHtmlWithVars(RestRequest req, RestResponse res, String name) throws IOException {
307      return req.getVarResolverSession().resolve(loadHtml(name));
308   }
309}