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