package org.rendersnake; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; /** * StringResource holds a cache for text content such as Html snippets that is * read from resources on the classpath. * <p> * A StringResource instance encapsulates such a content for rendering on a * html canvas. * <p> * Examples * </p> * * <pre> * html.text(StringResource.get("content/Introduction.html"), false); * html.render(new StringResource("content/Introduction.html")); * </pre> * * @author e.micklei */ public class StringResource implements Renderable { /** * Cache of content read from resources on the classpath. Unlimited size; no eviction or expiration. */ private final static Map<String, String> CACHE = new ConcurrentHashMap<String, String>(); /** * Intention revealing constant to emphasize that caching for an item is not needed */ public final static boolean DO_NOT_CACHE = false; /** * Cached value for a given location (by constructor) */ private String content; /** * If true then the content will be HTML-escaped when writing on a html */ private boolean escapeNeeded = false; /** * Create a StringResource with the cached value read from the resource location * @param location */ public StringResource(String location) { this.content = get(location); } /** * Create a StringResource with the cached value read from the resource location. * @param location * @param escapeNeeded */ public StringResource(String location, boolean escapeNeeded) { this.content = get(location); this.escapeNeeded = escapeNeeded; } /** * Access the String contents from the given resource location. * The contents is cached by StringResource. Use get(location,false); for non-cached access. * @param location * @return resource contents , never null */ public static String get(String location) { return get(location, true); } /** * Forget about the cached resource at the location. Ignore if missing. * @param location */ public static void flush(String location) { CACHE.remove(location); } /** * Forget about all cached resources. */ public static void flush() { CACHE.clear(); } /** * Access the String contents from the given resource location. * @param location * @param doCache * @return resource contents , never null */ public static String get(String location, boolean doCache) { String content = null; if (doCache) { content = CACHE.get(location); } if (content == null) { content = contentOrNull(location); if (content == null) { content = "[StringResource] Missing or error reading resource:" + location; } else { if (doCache) { CACHE.put(location, content); } } } return content; } /** * Return a StringResource for rendering the text available at <code>location</code> * @param location , the file on the classpath containing the resource text * @return a new StringResource */ public static StringResource valueOf(String location) { return new StringResource(location); } /** * Use the resource location to read the String contents. * @param location * @return String | null if resource is not available */ @SuppressWarnings("PMD.EmptyCatchBlock") private static String contentOrNull(String location) { if (location.startsWith("http://")) { return fetchContentOrNull(location); } String content = null; InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(location); if (is == null) { // retry different loader is = StringResource.class.getResourceAsStream(location); } if (is != null) { try { BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder builder = new StringBuilder(256); while (reader.ready()) builder.append(reader.readLine()).append('\n'); content = builder.toString(); } catch (IOException e) { Logger.getAnonymousLogger().log(Level.WARNING, "Unable to fetch content from:"+ location); // eat it, content stays null } } return content; } private static String fetchContentOrNull(String urlToRead) { // slow but standard URL url; HttpURLConnection conn; BufferedReader rd = null; String line; StringBuilder result = new StringBuilder(); try { url = new URL(urlToRead); conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); while ((line = rd.readLine()) != null) { result.append(line); } } catch (Exception e) { Logger.getAnonymousLogger().log(Level.WARNING, "Unable to fetch content from:"+ urlToRead); // eat it, content will be null return null; } finally { if (rd != null) try { rd.close(); } catch (Exception ex) {}; } return result.toString(); } /** * Write the content as text using the html. Escape if needed. */ public void renderOn(HtmlCanvas html) throws IOException { html.write(content, escapeNeeded); } }