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.rest.annotation.*;
020import org.apache.juneau.utils.*;
021
022/**
023 * Defines an interface for resolvers of <js>"$W{...}"</js> string variables.
024 * 
025 * <p>
026 * Widgets are associated with resources through the following
027 * <ul>
028 *    <li class='ja'>{@link HtmlDoc#widgets() @HtmlDoc.widgets}
029 *    <li class='jm'>{@link RestContextBuilder#widgets(Class...)}
030 *    <li class='jm'>{@link RestContextBuilder#widgets(Widget...)}
031 * </ul>
032 * 
033 * <p>
034 * Widgets allow you to add arbitrary HTML, CSS, and Javascript to the page.
035 * 
036 * <p>
037 * The HTML content returned by the {@link #getHtml(RestRequest)} method is added where the <js>"$W{...}"</js> is
038 * referenced in the page.
039 * The Javascript and stylesheet content is added to the header of the page.
040 * They allow you to control the look and behavior of your widgets.
041 * 
042 * <p>
043 * The following examples shows how to associate a widget with a REST method and then have it rendered in the links
044 * and aside section of the page:
045 * 
046 * <p class='bcode'>
047 *    <ja>@RestMethod</ja>(
048 *       widgets={
049 *          MyWidget.<jk>class</jk>
050 *       }
051 *       htmldoc=<ja>@HtmlDoc</ja>(
052 *          navlinks={
053 *             <js>"$W{MyWidget}"</js>
054 *          },
055 *          aside={
056 *             <js>"Check out this widget:  $W{MyWidget}"</js>
057 *          }
058 *       )
059 *    )
060 * </p>
061 * 
062 * <p>
063 * The following shows an example of a widget that renders an image located in the <code>htdocs</code> static files
064 * directory in your classpath (see {@link RestResource#staticFiles() @RestResource.staticFiles()}):
065 * <p class='bcode'>
066 *    <jk>public class</jk> MyWidget <jk>extends</jk> Widget {
067 * 
068 *       <ja>@Override</ja>
069 *       <jk>public</jk> String getHtml(RestRequest req) <jk>throws</jk> Exception {
070 *          UriResolver r = req.getUriResolver();
071 *          <jk>return</jk> <js>"&lt;img class='myimage' onclick='myalert(this)' src='"</js>+r.resolve(<js>"servlet:/htdocs/myimage.png"</js>)+<js>"'&gt;"</js>;
072 *       }
073 * 
074 *       <ja>@Override</ja>
075 *       <jk>public</jk> String getScript(RestRequest req) <jk>throws</jk> Exception {
076 *          <jk>return</jk> <js>""</js>
077 *             + <js>"\n function myalert(imageElement) {"</js>
078 *             + <js>"\n   alert('cool!');"</js>
079 *             + <js>"\n }"</js>;
080 *       }
081 * 
082 *       <ja>@Override</ja>
083 *       <jk>public</jk> String getStyle(RestRequest req) <jk>throws</jk> Exception {
084 *          <jk>return</jk> <js>""</js>
085 *             + <js>"\n .myimage {"</js>
086 *             + <js>"\n   border: 10px solid red;"</js>
087 *             + <js>"\n }"</js>;
088 *       }
089 *    }
090 * </p>
091 * 
092 * <p>
093 * Note the {@link #getClasspathResourceAsString(String)} and {@link #getClasspathResourceAsString(String, Locale)} convenience methods
094 * provided for quickly loading javascript and css files from the classpath or file system.
095 * These are useful if your script or styles are complex and you want them loaded from files.
096 * 
097 * <p>
098 * <p class='bcode'>
099 *    <jk>public class</jk> MyWidget <jk>extends</jk> Widget {
100 * 
101 *       ...
102 * 
103 *       <ja>@Override</ja>
104 *       <jk>public</jk> String getScript(RestRequest req) <jk>throws</jk> Exception {
105 *          <jk>return</jk> getResourceAsString(<js>"MyWidget.js"</js>);
106 *       }
107 * 
108 *       <ja>@Override</ja>
109 *       <jk>public</jk> String getStyle(RestRequest req) <jk>throws</jk> Exception {
110 *          <jk>return</jk> getResourceAsString(<js>"MyWidget.css"</js>);
111 *       }
112 *    }
113 * </p>
114 * 
115 * <p>
116 * Widgets must provide one of the following public constructors:
117 * <ul>
118 *    <li><code><jk>public</jk> Widget();</code>
119 *    <li><code><jk>public</jk> Widget(PropertyStore);</code>
120 * </ul>
121 * 
122 * <p>
123 * Widgets can be defined as inner classes of REST resource classes.
124 * 
125 * <h5 class='section'>See Also:</h5>
126 * <ul>
127 *    <li class='link'><a class="doclink" href="../../../../../overview-summary.html#juneau-rest-server.Widgets">Overview &gt; juneau-rest-server &gt; Widgets</a>
128 * </ul>
129 */
130public abstract class Widget {
131
132   private final ClasspathResourceManager rm = new ClasspathResourceManager(getClass());
133
134   /**
135    * The widget key.
136    * 
137    * <p>
138    * (i.e. The variable name inside the <js>"$W{...}"</js> variable).
139    * 
140    * <p>
141    * The returned value must not be <jk>null</jk>.
142    * 
143    * <p>
144    * If not overridden, the default value is the class simple name.
145    * 
146    * @return The widget key.
147    */
148   public String getName() {
149      return getClass().getSimpleName();
150   }
151
152   /**
153    * Resolves the HTML content for this widget.
154    * 
155    * <p>
156    * A returned value of <jk>null</jk> will cause nothing to be added to the page.
157    * 
158    * @param req The HTTP request object.
159    * @return The HTML content of this widget.
160    * @throws Exception
161    */
162   public String getHtml(RestRequest req) throws Exception {
163      return null;
164   }
165
166   /**
167    * Resolves any Javascript that should be added to the <xt>&lt;head&gt;/&lt;script&gt;</xt> element.
168    * 
169    * <p>
170    * A returned value of <jk>null</jk> will cause nothing to be added to the page.
171    * 
172    * @param req The HTTP request object.
173    * @return The Javascript needed by this widget.
174    * @throws Exception
175    */
176   public String getScript(RestRequest req) throws Exception {
177      return null;
178   }
179
180   /**
181    * Resolves any CSS styles that should be added to the <xt>&lt;head&gt;/&lt;style&gt;</xt> element.
182    * 
183    * <p>
184    * A returned value of <jk>null</jk> will cause nothing to be added to the page.
185    * 
186    * @param req The HTTP request object.
187    * @return The CSS styles needed by this widget.
188    * @throws Exception
189    */
190   public String getStyle(RestRequest req) throws Exception {
191      return null;
192   }
193
194   /**
195    * Retrieves the specified classpath resource and returns the contents as a string.
196    * 
197    * <p>
198    * Same as {@link Class#getResourceAsStream(String)} except if it doesn't find the resource on this class, searches
199    * up the parent hierarchy chain.
200    * 
201    * <p>
202    * If the resource cannot be found in the classpath, then an attempt is made to look relative to the JVM working directory.
203    * <br>Path traversals outside the working directory are not allowed for security reasons.
204    * 
205    * @param name Name of the desired resource.
206    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
207    * @throws IOException
208    */
209   protected String getClasspathResourceAsString(String name) throws IOException {
210      return rm.getString(name);
211   }
212
213   /**
214    * Same as {@link #getClasspathResourceAsString(String)} except also looks for localized-versions of the file.
215    * 
216    * <p>
217    * If the <code>locale</code> is specified, then we look for resources whose name matches that locale.
218    * <br>For example, if looking for the resource <js>"MyResource.txt"</js> for the Japanese locale, we will look for
219    * files in the following order:
220    * <ol>
221    *    <li><js>"MyResource_ja_JP.txt"</js>
222    *    <li><js>"MyResource_ja.txt"</js>
223    *    <li><js>"MyResource.txt"</js>
224    * </ol>
225    * 
226    * @param name Name of the desired resource.
227    * @param locale The locale.  Can be <jk>null</jk>.
228    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
229    * @throws IOException
230    */
231   protected String getClasspathResourceAsString(String name, Locale locale) throws IOException {
232      return rm.getString(name, locale);
233   }
234
235   /**
236    * Convenience method for calling {@link #getClasspathResourceAsString(String)} except also strips Javascript comments from
237    * the file.
238    * 
239    * <p>
240    * Comments are assumed to be Java-style block comments: <js>"/*"</js>.
241    * 
242    * @param name Name of the desired resource.
243    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
244    * @throws IOException
245    */
246   protected String loadScript(String name) throws IOException {
247      String s = getClasspathResourceAsString(name);
248      if (s != null)
249         s = s.replaceAll("(?s)\\/\\*(.*?)\\*\\/\\s*", "");
250      return s;
251   }
252
253   /**
254    * Convenience method for calling {@link #getClasspathResourceAsString(String)} except also strips CSS comments from
255    * the file.
256    * 
257    * <p>
258    * Comments are assumed to be Java-style block comments: <js>"/*"</js>.
259    * 
260    * @param name Name of the desired resource.
261    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
262    * @throws IOException
263    */
264   protected String loadStyle(String name) throws IOException {
265      String s = getClasspathResourceAsString(name);
266      if (s != null)
267         s = s.replaceAll("(?s)\\/\\*(.*?)\\*\\/\\s*", "");
268      return s;
269   }
270
271   /**
272    * Convenience method for calling {@link #getClasspathResourceAsString(String)} except also strips HTML comments from the
273    * file.
274    * 
275    * <p>
276    * Comment are assumed to be <js>"<!-- -->"</js> code blocks.
277    * 
278    * @param name Name of the desired resource.
279    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
280    * @throws IOException
281    */
282   protected String loadHtml(String name) throws IOException {
283      String s = getClasspathResourceAsString(name);
284      if (s != null)
285         s = s.replaceAll("(?s)<!--(.*?)-->\\s*", "");
286      return s;
287   }
288}