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>"<img class='myimage' onclick='myalert(this)' src='"</js>+r.resolve(<js>"servlet:/htdocs/myimage.png"</js>)+<js>"'>"</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 > juneau-rest-server > 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><head>/<script></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><head>/<style></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}