001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.widget;
018
019import static org.apache.juneau.common.utils.ThrowableUtils.*;
020
021import java.io.*;
022
023import org.apache.juneau.cp.*;
024import org.apache.juneau.html.*;
025import org.apache.juneau.http.response.*;
026import org.apache.juneau.rest.*;
027import org.apache.juneau.svl.*;
028
029/**
030 * Defines an interface for resolvers of <js>"$W{...}"</js> string variables.
031 *
032 * <p>
033 * Widgets must provide one of the following public constructors:
034 * <ul>
035 *    <li><code><jk>public</jk> Widget();</code>
036 *    <li><code><jk>public</jk> Widget(ContextProperties);</code>
037 * </ul>
038 *
039 * <p>
040 * Widgets can be defined as inner classes of REST resource classes.
041 *
042 * <h5 class='section'>See Also:</h5><ul>
043 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HtmlPredefinedWidgets">Predefined Widgets</a>
044 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HtmlWidgets">Widgets</a>
045 * </ul>
046 */
047public abstract class Widget implements HtmlWidget {
048
049   /**
050    * The widget key.
051    *
052    * <p>
053    * (i.e. The variable name inside the <js>"$W{...}"</js> variable).
054    *
055    * <p>
056    * The returned value must not be <jk>null</jk>.
057    *
058    * <p>
059    * If not overridden, the default value is the class simple name.
060    *
061    * @return The widget key.
062    */
063   @Override
064   public String getName() {
065      return getClass().getSimpleName();
066   }
067
068   private RestRequest req(VarResolverSession session) {
069      return session.getBean(RestRequest.class).orElseThrow(InternalServerError::new);
070   }
071
072   private RestResponse res(VarResolverSession session) {
073      return session.getBean(RestResponse.class).orElseThrow(InternalServerError::new);
074   }
075
076   @Override /* HtmlWidget */
077   public String getHtml(VarResolverSession session) {
078      return getHtml(req(session), res(session));
079   }
080
081   @Override /* HtmlWidget */
082   public String getScript(VarResolverSession session) {
083      return getScript(req(session), res(session));
084   }
085
086   @Override /* HtmlWidget */
087   public String getStyle(VarResolverSession session) {
088      return getStyle(req(session), res(session));
089   }
090
091   /**
092    * Resolves the HTML content for this widget.
093    *
094    * <p>
095    * A returned value of <jk>null</jk> will cause nothing to be added to the page.
096    *
097    * @param req The HTTP request object.
098    * @param res The current HTTP response.
099    * @return The HTML content of this widget.
100    */
101   public String getHtml(RestRequest req, RestResponse res) {
102      return null;
103   }
104
105   /**
106    * Resolves any Javascript that should be added to the <xt>&lt;head&gt;/&lt;script&gt;</xt> element.
107    *
108    * <p>
109    * A returned value of <jk>null</jk> will cause nothing to be added to the page.
110    *
111    * @param req The HTTP request object.
112    * @param res The current HTTP response.
113    * @return The Javascript needed by this widget.
114    */
115   public String getScript(RestRequest req, RestResponse res) {
116      return null;
117   }
118
119   /**
120    * Resolves any CSS styles that should be added to the <xt>&lt;head&gt;/&lt;style&gt;</xt> element.
121    *
122    * <p>
123    * A returned value of <jk>null</jk> will cause nothing to be added to the page.
124    *
125    * @param req The HTTP request object.
126    * @param res The current HTTP response.
127    * @return The CSS styles needed by this widget.
128    */
129   public String getStyle(RestRequest req, RestResponse res) {
130      return null;
131   }
132
133   /**
134    * Returns the file finder to use for finding files on the file system.
135    *
136    * @param req The HTTP request object.
137    * @return The file finder to used for finding files on the file system.
138    */
139   protected FileFinder getFileFinder(RestRequest req) {
140      return req.getStaticFiles();
141   }
142
143   /**
144    * Loads the specified javascript file and strips any Javascript comments from the file.
145    *
146    * <p>
147    * Comments are assumed to be Java-style block comments: <js>"/*"</js>.
148    *
149    * @param req The HTTP request object.
150    * @param name Name of the desired resource.
151    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
152    */
153   protected String loadScript(RestRequest req, String name) {
154      try {
155         String s = getFileFinder(req).getString(name, null).orElse(null);
156         if (s != null)
157            s = s.replaceAll("(?s)\\/\\*(.*?)\\*\\/\\s*", "");
158         return s;
159      } catch (IOException e) {
160         throw new UncheckedIOException(e);
161      }
162   }
163
164   /**
165    * Same as {@link #loadScript(RestRequest,String)} but replaces request-time SVL variables.
166    *
167    * <h5 class='section'>See Also:</h5><ul>
168    *    <li class='jm'>{@link org.apache.juneau.rest.RestContext#getVarResolver()}
169    *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerSvlVariables">SVL Variables</a>
170    * </ul>
171    *
172    * @param req The current HTTP request.
173    * @param res The current HTTP response.
174    * @param name Name of the desired resource.
175    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
176    * @throws IOException Thrown by underlying stream.
177    */
178   protected String loadScriptWithVars(RestRequest req, RestResponse res, String name) throws IOException {
179      return req.getVarResolverSession().resolve(loadScript(req, name));
180   }
181
182   /**
183    * Loads the specified CSS file and strips CSS comments from the file.
184    *
185    * <p>
186    * Comments are assumed to be Java-style block comments: <js>"/*"</js>.
187    *
188    * @param req The HTTP request object.
189    * @param name Name of the desired resource.
190    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
191    */
192   protected String loadStyle(RestRequest req, String name) {
193      try {
194         String s = getFileFinder(req).getString(name, null).orElse(null);
195         if (s != null)
196            s = s.replaceAll("(?s)\\/\\*(.*?)\\*\\/\\s*", "");
197         return s;
198      } catch (IOException e) {
199         throw asRuntimeException(e);
200      }
201   }
202
203   /**
204    * Same as {@link #loadStyle(RestRequest,String)} but replaces request-time SVL variables.
205    *
206    * <h5 class='section'>See Also:</h5><ul>
207    *    <li class='jm'>{@link org.apache.juneau.rest.RestContext#getVarResolver()}
208    *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerSvlVariables">SVL Variables</a>
209    * </ul>
210    *
211    * @param req The current HTTP request.
212    * @param res The current HTTP response.
213    * @param name Name of the desired resource.
214    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
215    * @throws IOException Thrown by underlying stream.
216    */
217   protected String loadStyleWithVars(RestRequest req, RestResponse res, String name) throws IOException {
218      return req.getVarResolverSession().resolve(loadStyle(req, name));
219   }
220
221   /**
222    * Loads the specified HTML file and strips HTML comments from the file.
223    *
224    * <p>
225    * Comment are assumed to be <js>"&lt;!-- --&gt;"</js> code blocks.
226    *
227    * @param req The HTTP request object.
228    * @param name Name of the desired resource.
229    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
230    */
231   protected String loadHtml(RestRequest req, String name) {
232      try {
233         String s = getFileFinder(req).getString(name, null).orElse(null);
234         if (s != null)
235            s = s.replaceAll("(?s)<!--(.*?)-->\\s*", "");
236         return s;
237      } catch (IOException e) {
238         throw asRuntimeException(e);
239      }
240   }
241
242   /**
243    * Same as {@link #loadHtml(RestRequest,String)} but replaces request-time SVL variables.
244    *
245    * <h5 class='section'>See Also:</h5><ul>
246    *    <li class='jm'>{@link org.apache.juneau.rest.RestContext#getVarResolver()}
247    *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerSvlVariables">SVL Variables</a>
248    * </ul>
249    *
250    * @param req The current HTTP request.
251    * @param res The current HTTP response.
252    * @param name Name of the desired resource.
253    * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
254    * @throws IOException Thrown by underlying stream.
255    */
256   protected String loadHtmlWithVars(RestRequest req, RestResponse res, String name) throws IOException {
257      return req.getVarResolverSession().resolve(loadHtml(req, name));
258   }
259}