| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.click.util; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.Closeable; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.OutputStream; |
| import java.io.Serializable; |
| import java.io.UnsupportedEncodingException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.net.URL; |
| import java.net.URLDecoder; |
| import java.net.URLEncoder; |
| import java.security.MessageDigest; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.MissingResourceException; |
| import java.util.ResourceBundle; |
| import java.util.TreeMap; |
| import java.util.zip.GZIPInputStream; |
| import java.util.zip.GZIPOutputStream; |
| |
| import javax.servlet.ServletContext; |
| import javax.servlet.http.Cookie; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| |
| import org.apache.click.Context; |
| import org.apache.click.Control; |
| import org.apache.click.Page; |
| import org.apache.click.ActionResult; |
| import org.apache.click.Stateful; |
| import org.apache.click.control.AbstractControl; |
| import org.apache.click.control.AbstractLink; |
| import org.apache.click.control.ActionLink; |
| import org.apache.click.control.Container; |
| import org.apache.click.control.Field; |
| import org.apache.click.control.Form; |
| import org.apache.click.service.ConfigService; |
| import org.apache.click.service.LogService; |
| |
| import org.apache.commons.codec.binary.Base64; |
| import org.apache.commons.fileupload.servlet.ServletFileUpload; |
| import org.apache.commons.io.IOUtils; |
| import org.apache.commons.lang.ClassUtils; |
| import org.apache.commons.lang.StringUtils; |
| import org.apache.commons.lang.Validate; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.EntityResolver; |
| |
| /** |
| * Provides miscellaneous Form, String and Stream utility methods. |
| */ |
| public class ClickUtils { |
| |
| // ------------------------------------------------------- Public Constants |
| |
| /** |
| * The resource <tt>versioning</tt> request attribute: key: |
| * <tt>enable-resource-version</tt>. |
| * <p/> |
| * If this attribute is set to <tt>true</tt> and Click is running in |
| * <tt>production</tt> or <tt>profile</tt> mode, Click resources returned |
| * from {@link org.apache.click.Control#getHeadElements()} will have a |
| * <tt>version indicator</tt> added to their path. |
| * |
| * @see org.apache.click.Control#getHeadElements() |
| * @see org.apache.click.util.ClickUtils#getResourceVersionIndicator(Context) |
| */ |
| public static final String ENABLE_RESOURCE_VERSION = "enable-resource-version"; |
| |
| /** |
| * The default Click configuration filename: |
| * "<tt>/WEB-INF/click.xml</tt>". |
| */ |
| public static final String DEFAULT_APP_CONFIG = "/WEB-INF/click.xml"; |
| |
| /** The version indicator separator string. */ |
| public static final String VERSION_INDICATOR_SEP = "_"; |
| |
| /** The static web resource version number indicator string. */ |
| public static final String RESOURCE_VERSION_INDICATOR = |
| VERSION_INDICATOR_SEP + getClickVersion(); |
| |
| // ------------------------------------------------------ Private Constants |
| |
| /** The cached resource version indicator. */ |
| private static String cachedResourceVersionIndicator; |
| |
| /** The static application-wide resource version indicator. */ |
| private static String applicationVersion; |
| |
| /** The cached application version indicator string. */ |
| private static String cachedApplicationVersionIndicator; |
| |
| /** |
| * Character used to separate username and password in persistent cookies. |
| * 0x13 == "Device Control 3" non-printing ASCII char. Unlikely to appear |
| * in a username |
| */ |
| private static final char DELIMITER = 0x13; |
| |
| /* |
| * "Tweakable" parameters for the cookie encoding. NOTE: changing these |
| * and recompiling this class will essentially invalidate old cookies. |
| */ |
| private final static char ENCODE_CHAR_OFFSET1 = 'C'; |
| private final static char ENCODE_CHAR_OFFSET2 = 'i'; |
| |
| /** Hexadecimal characters for MD5 encoding. */ |
| private static final char[] HEXADECIMAL = { '0', '1', '2', '3', '4', '5', |
| '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; |
| |
| /** Ajax request header or parameter: "<tt>X-Requested-With</tt>". */ |
| private static final String X_REQUESTED_WITH = "X-Requested-With"; |
| |
| /** |
| * The array of escaped HTML character values, indexed on char value. |
| * <p/> |
| * HTML entities values were derived from Jakarta Commons Lang |
| * <tt>org.apache.commons.lang.Entities</tt> class. |
| */ |
| private static final String[] HTML_ENTITIES = new String[9999]; |
| |
| static { |
| HTML_ENTITIES[34] = """; // " - double-quote |
| HTML_ENTITIES[38] = "&"; // & - ampersand |
| HTML_ENTITIES[60] = "<"; // < - less-than |
| HTML_ENTITIES[62] = ">"; // > - greater-than |
| HTML_ENTITIES[160] = " "; // non-breaking space |
| HTML_ENTITIES[161] = "¡"; // inverted exclamation mark |
| HTML_ENTITIES[162] = "¢"; // cent sign |
| HTML_ENTITIES[163] = "£"; // pound sign |
| HTML_ENTITIES[164] = "¤"; // currency sign |
| HTML_ENTITIES[165] = "¥"; // yen sign = yuan sign |
| HTML_ENTITIES[166] = "¦"; // broken bar = broken vertical bar |
| HTML_ENTITIES[167] = "§"; // section sign |
| HTML_ENTITIES[168] = "¨"; // diaeresis = spacing diaeresis |
| HTML_ENTITIES[169] = "©"; // copyright sign |
| HTML_ENTITIES[170] = "ª"; // feminine ordinal indicator |
| HTML_ENTITIES[171] = "«"; // left-pointing double angle quotation mark = left pointing guillemet |
| HTML_ENTITIES[172] = "¬"; // not sign |
| HTML_ENTITIES[173] = "­"; // soft hyphen = discretionary hyphen |
| HTML_ENTITIES[174] = "®"; // registered trademark sign |
| HTML_ENTITIES[175] = "¯"; //macron = spacing macron = overline = APL overbar |
| HTML_ENTITIES[176] = "°"; //degree sign |
| HTML_ENTITIES[177] = "±"; //plus-minus sign = plus-or-minus sign |
| HTML_ENTITIES[178] = "²"; //superscript two = superscript digit two = squared |
| HTML_ENTITIES[179] = "³"; //superscript three = superscript digit three = cubed |
| HTML_ENTITIES[180] = "´"; //acute accent = spacing acute |
| HTML_ENTITIES[181] = "µ"; //micro sign |
| HTML_ENTITIES[182] = "¶"; //pilcrow sign = paragraph sign |
| HTML_ENTITIES[183] = "·"; //middle dot = Georgian comma = Greek middle dot |
| HTML_ENTITIES[184] = "¸"; //cedilla = spacing cedilla |
| HTML_ENTITIES[185] = "¹"; //superscript one = superscript digit one |
| HTML_ENTITIES[186] = "º"; //masculine ordinal indicator |
| HTML_ENTITIES[187] = "»"; //right-pointing double angle quotation mark = right pointing guillemet |
| HTML_ENTITIES[188] = "¼"; //vulgar fraction one quarter = fraction one quarter |
| HTML_ENTITIES[189] = "½"; //vulgar fraction one half = fraction one half |
| HTML_ENTITIES[190] = "¾"; //vulgar fraction three quarters = fraction three quarters |
| HTML_ENTITIES[191] = "¿"; //inverted question mark = turned question mark |
| HTML_ENTITIES[192] = "À"; // À - uppercase A, grave accent |
| HTML_ENTITIES[193] = "Á"; // � - uppercase A, acute accent |
| HTML_ENTITIES[194] = "Â"; // Â - uppercase A, circumflex accent |
| HTML_ENTITIES[195] = "Ã"; // Ã - uppercase A, tilde |
| HTML_ENTITIES[196] = "Ä"; // Ä - uppercase A, umlaut |
| HTML_ENTITIES[197] = "Å"; // Ã… - uppercase A, ring |
| HTML_ENTITIES[198] = "Æ"; // Æ- uppercase AE |
| HTML_ENTITIES[199] = "Ç"; // Ç - uppercase C, cedilla |
| HTML_ENTITIES[200] = "È"; // È - uppercase E, grave accent |
| HTML_ENTITIES[201] = "É"; // É - uppercase E, acute accent |
| HTML_ENTITIES[202] = "Ê"; // Ê- uppercase E, circumflex accent |
| HTML_ENTITIES[203] = "Ë"; // Ë - uppercase E, umlaut |
| HTML_ENTITIES[204] = "Ì"; // ÃŒ - uppercase I, grave accent |
| HTML_ENTITIES[205] = "Í"; // � - uppercase I, acute accent |
| HTML_ENTITIES[206] = "Î"; // ÃŽ - uppercase I, circumflex accent |
| HTML_ENTITIES[207] = "Ï"; // � - uppercase I, umlaut |
| HTML_ENTITIES[208] = "Ð"; // � - uppercase Eth, Icelandic |
| HTML_ENTITIES[209] = "Ñ"; // Ñ - uppercase N, tilde |
| HTML_ENTITIES[210] = "Ò"; // Ã’ - uppercase O, grave accent |
| HTML_ENTITIES[211] = "Ó"; // Ó - uppercase O, acute accent |
| HTML_ENTITIES[212] = "Ô"; // �? - uppercase O, circumflex accent |
| HTML_ENTITIES[213] = "Õ"; // Õ - uppercase O, tilde |
| HTML_ENTITIES[214] = "Ö"; // Ö - uppercase O, umlaut |
| HTML_ENTITIES[215] = "×"; //multiplication sign |
| HTML_ENTITIES[216] = "Ø"; // Ø - uppercase O, slash |
| HTML_ENTITIES[217] = "Ù"; // Ù - uppercase U, grave accent |
| HTML_ENTITIES[218] = "Ú"; // Ú - uppercase U, acute accent |
| HTML_ENTITIES[219] = "Û"; // Û - uppercase U, circumflex accent |
| HTML_ENTITIES[220] = "Ü"; // Ãœ - uppercase U, umlaut |
| HTML_ENTITIES[221] = "Ý"; // � - uppercase Y, acute accent |
| HTML_ENTITIES[222] = "Þ"; // Þ - uppercase THORN, Icelandic |
| HTML_ENTITIES[223] = "ß"; // ß - lowercase sharps, German |
| HTML_ENTITIES[224] = "à"; // à- lowercase a, grave accent |
| HTML_ENTITIES[225] = "á"; // á - lowercase a, acute accent |
| HTML_ENTITIES[226] = "â"; // â - lowercase a, circumflex accent |
| HTML_ENTITIES[227] = "ã"; // ã - lowercase a, tilde |
| HTML_ENTITIES[228] = "ä"; // ä - lowercase a, umlaut |
| HTML_ENTITIES[229] = "å"; // Ã¥ - lowercase a, ring |
| HTML_ENTITIES[230] = "æ"; // æ - lowercase ae |
| HTML_ENTITIES[231] = "ç"; // ç - lowercase c, cedilla |
| HTML_ENTITIES[232] = "è"; // è - lowercase e, grave accent |
| HTML_ENTITIES[233] = "é"; // é - lowercase e, acute accent |
| HTML_ENTITIES[234] = "ê"; // ê - lowercase e, circumflex accent |
| HTML_ENTITIES[235] = "ë"; // ë - lowercase e, umlaut |
| HTML_ENTITIES[236] = "ì"; // ì - lowercase i, grave accent |
| HTML_ENTITIES[237] = "í"; // à- lowercase i, acute accent |
| HTML_ENTITIES[238] = "î"; // î - lowercase i, circumflex accent |
| HTML_ENTITIES[239] = "ï"; // ï - lowercase i, umlaut |
| HTML_ENTITIES[240] = "ð"; // ð - lowercase eth, Icelandic |
| HTML_ENTITIES[241] = "ñ"; // ñ - lowercase n, tilde |
| HTML_ENTITIES[242] = "ò"; // ò - lowercase o, grave accent |
| HTML_ENTITIES[243] = "ó"; // ó - lowercase o, acute accent |
| HTML_ENTITIES[244] = "ô"; // ô - lowercase o, circumflex accent |
| HTML_ENTITIES[245] = "õ"; // õ - lowercase o, tilde |
| HTML_ENTITIES[246] = "ö"; // ö - lowercase o, umlaut |
| HTML_ENTITIES[247] = "÷"; // division sign |
| HTML_ENTITIES[248] = "ø"; // ø - lowercase o, slash |
| HTML_ENTITIES[249] = "ù"; // ù - lowercase u, grave accent |
| HTML_ENTITIES[250] = "ú"; // ú - lowercase u, acute accent |
| HTML_ENTITIES[251] = "û"; // û - lowercase u, circumflex accent |
| HTML_ENTITIES[252] = "ü"; // ü - lowercase u, umlaut |
| HTML_ENTITIES[253] = "ý"; // ý - lowercase y, acute accent |
| HTML_ENTITIES[254] = "þ"; // þ - lowercase thorn, Icelandic |
| HTML_ENTITIES[255] = "ÿ"; // ÿ - lowercase y, umlaut |
| // http://www.w3.org/TR/REC-html40/sgml/entities.html |
| // <!-- Latin Extended-B --> |
| HTML_ENTITIES[402] = "ƒ"; //latin small f with hook = function= florin, U+0192 ISOtech --> |
| // <!-- Greek --> |
| HTML_ENTITIES[913] = "Α"; //greek capital letter alpha, U+0391 --> |
| HTML_ENTITIES[914] = "Β"; //greek capital letter beta, U+0392 --> |
| HTML_ENTITIES[915] = "Γ"; //greek capital letter gamma,U+0393 ISOgrk3 --> |
| HTML_ENTITIES[916] = "Δ"; //greek capital letter delta,U+0394 ISOgrk3 --> |
| HTML_ENTITIES[917] = "Ε"; //greek capital letter epsilon, U+0395 --> |
| HTML_ENTITIES[918] = "Ζ"; //greek capital letter zeta, U+0396 --> |
| HTML_ENTITIES[919] = "Η"; //greek capital letter eta, U+0397 --> |
| HTML_ENTITIES[920] = "Θ"; //greek capital letter theta,U+0398 ISOgrk3 --> |
| HTML_ENTITIES[921] = "Ι"; //greek capital letter iota, U+0399 --> |
| HTML_ENTITIES[922] = "Κ"; //greek capital letter kappa, U+039A --> |
| HTML_ENTITIES[923] = "Λ"; //greek capital letter lambda,U+039B ISOgrk3 --> |
| HTML_ENTITIES[924] = "Μ"; //greek capital letter mu, U+039C --> |
| HTML_ENTITIES[925] = "Ν"; //greek capital letter nu, U+039D --> |
| HTML_ENTITIES[926] = "Ξ"; //greek capital letter xi, U+039E ISOgrk3 --> |
| HTML_ENTITIES[927] = "Ο"; //greek capital letter omicron, U+039F --> |
| HTML_ENTITIES[928] = "Π"; //greek capital letter pi, U+03A0 ISOgrk3 --> |
| HTML_ENTITIES[929] = "Ρ"; //greek capital letter rho, U+03A1 --> |
| // <!-- there is no Sigmaf, and no U+03A2 character either --> |
| HTML_ENTITIES[931] = "Σ"; //greek capital letter sigma,U+03A3 ISOgrk3 --> |
| HTML_ENTITIES[932] = "Τ"; //greek capital letter tau, U+03A4 --> |
| HTML_ENTITIES[933] = "Υ"; //greek capital letter upsilon,U+03A5 ISOgrk3 --> |
| HTML_ENTITIES[934] = "Φ"; //greek capital letter phi,U+03A6 ISOgrk3 --> |
| HTML_ENTITIES[935] = "Χ"; //greek capital letter chi, U+03A7 --> |
| HTML_ENTITIES[936] = "Ψ"; //greek capital letter psi,U+03A8 ISOgrk3 --> |
| HTML_ENTITIES[937] = "Ω"; //greek capital letter omega,U+03A9 ISOgrk3 --> |
| HTML_ENTITIES[945] = "α"; //greek small letter alpha,U+03B1 ISOgrk3 --> |
| HTML_ENTITIES[946] = "β"; //greek small letter beta, U+03B2 ISOgrk3 --> |
| HTML_ENTITIES[947] = "γ"; //greek small letter gamma,U+03B3 ISOgrk3 --> |
| HTML_ENTITIES[948] = "δ"; //greek small letter delta,U+03B4 ISOgrk3 --> |
| HTML_ENTITIES[949] = "ε"; //greek small letter epsilon,U+03B5 ISOgrk3 --> |
| HTML_ENTITIES[950] = "ζ"; //greek small letter zeta, U+03B6 ISOgrk3 --> |
| HTML_ENTITIES[951] = "η"; //greek small letter eta, U+03B7 ISOgrk3 --> |
| HTML_ENTITIES[952] = "θ"; //greek small letter theta,U+03B8 ISOgrk3 --> |
| HTML_ENTITIES[953] = "ι"; //greek small letter iota, U+03B9 ISOgrk3 --> |
| HTML_ENTITIES[954] = "κ"; //greek small letter kappa,U+03BA ISOgrk3 --> |
| HTML_ENTITIES[955] = "λ"; //greek small letter lambda,U+03BB ISOgrk3 --> |
| HTML_ENTITIES[956] = "μ"; //greek small letter mu, U+03BC ISOgrk3 --> |
| HTML_ENTITIES[957] = "ν"; //greek small letter nu, U+03BD ISOgrk3 --> |
| HTML_ENTITIES[958] = "ξ"; //greek small letter xi, U+03BE ISOgrk3 --> |
| HTML_ENTITIES[959] = "ο"; //greek small letter omicron, U+03BF NEW --> |
| HTML_ENTITIES[960] = "π"; //greek small letter pi, U+03C0 ISOgrk3 --> |
| HTML_ENTITIES[961] = "ρ"; //greek small letter rho, U+03C1 ISOgrk3 --> |
| HTML_ENTITIES[962] = "ς"; //greek small letter final sigma,U+03C2 ISOgrk3 --> |
| HTML_ENTITIES[963] = "σ"; //greek small letter sigma,U+03C3 ISOgrk3 --> |
| HTML_ENTITIES[964] = "τ"; //greek small letter tau, U+03C4 ISOgrk3 --> |
| HTML_ENTITIES[965] = "υ"; //greek small letter upsilon,U+03C5 ISOgrk3 --> |
| HTML_ENTITIES[966] = "φ"; //greek small letter phi, U+03C6 ISOgrk3 --> |
| HTML_ENTITIES[967] = "χ"; //greek small letter chi, U+03C7 ISOgrk3 --> |
| HTML_ENTITIES[968] = "ψ"; //greek small letter psi, U+03C8 ISOgrk3 --> |
| HTML_ENTITIES[969] = "ω"; //greek small letter omega,U+03C9 ISOgrk3 --> |
| HTML_ENTITIES[977] = "ϑ"; //greek small letter theta symbol,U+03D1 NEW --> |
| HTML_ENTITIES[978] = "ϒ"; //greek upsilon with hook symbol,U+03D2 NEW --> |
| HTML_ENTITIES[982] = "ϖ"; //greek pi symbol, U+03D6 ISOgrk3 --> |
| // <!-- General Punctuation --> |
| HTML_ENTITIES[8226] = "•"; //bullet = black small circle,U+2022 ISOpub --> |
| // <!-- bullet is NOT the same as bullet operator, U+2219 --> |
| HTML_ENTITIES[8230] = "…"; //horizontal ellipsis = three dot leader,U+2026 ISOpub --> |
| HTML_ENTITIES[8242] = "′"; //prime = minutes = feet, U+2032 ISOtech --> |
| HTML_ENTITIES[8243] = "″"; //double prime = seconds = inches,U+2033 ISOtech --> |
| HTML_ENTITIES[8254] = "‾"; //overline = spacing overscore,U+203E NEW --> |
| HTML_ENTITIES[8260] = "⁄"; //fraction slash, U+2044 NEW --> |
| // <!-- Letterlike Symbols --> |
| HTML_ENTITIES[8472] = "℘"; //script capital P = power set= Weierstrass p, U+2118 ISOamso --> |
| HTML_ENTITIES[8465] = "ℑ"; //blackletter capital I = imaginary part,U+2111 ISOamso --> |
| HTML_ENTITIES[8476] = "ℜ"; //blackletter capital R = real part symbol,U+211C ISOamso --> |
| HTML_ENTITIES[8482] = "™"; //trade mark sign, U+2122 ISOnum --> |
| HTML_ENTITIES[8501] = "ℵ"; //alef symbol = first transfinite cardinal,U+2135 NEW --> |
| // <!-- alef symbol is NOT the same as hebrew letter alef,U+05D0 although the same glyph could be used to depict both characters --> |
| // <!-- Arrows --> |
| HTML_ENTITIES[8592] = "←"; //leftwards arrow, U+2190 ISOnum --> |
| HTML_ENTITIES[8593] = "↑"; //upwards arrow, U+2191 ISOnum--> |
| HTML_ENTITIES[8594] = "→"; //rightwards arrow, U+2192 ISOnum --> |
| HTML_ENTITIES[8595] = "↓"; //downwards arrow, U+2193 ISOnum --> |
| HTML_ENTITIES[8596] = "↔"; //left right arrow, U+2194 ISOamsa --> |
| HTML_ENTITIES[8629] = "↵"; //downwards arrow with corner leftwards= carriage return, U+21B5 NEW --> |
| HTML_ENTITIES[8656] = "⇐"; //leftwards double arrow, U+21D0 ISOtech --> |
| // <!-- ISO 10646 does not say that lArr is the same as the 'is implied by' arrowbut also does not have any other character for that function. So ? lArr canbe used for 'is implied by' as ISOtech suggests --> |
| HTML_ENTITIES[8657] = "⇑"; //upwards double arrow, U+21D1 ISOamsa --> |
| HTML_ENTITIES[8658] = "⇒"; //rightwards double arrow,U+21D2 ISOtech --> |
| // <!-- ISO 10646 does not say this is the 'implies' character but does not have another character with this function so ?rArr can be used for 'implies' as ISOtech suggests --> |
| HTML_ENTITIES[8659] = "⇓"; //downwards double arrow, U+21D3 ISOamsa --> |
| HTML_ENTITIES[8660] = "⇔"; //left right double arrow,U+21D4 ISOamsa --> |
| // <!-- Mathematical Operators --> |
| HTML_ENTITIES[8704] = "∀"; //for all, U+2200 ISOtech --> |
| HTML_ENTITIES[8706] = "∂"; //partial differential, U+2202 ISOtech --> |
| HTML_ENTITIES[8707] = "∃"; //there exists, U+2203 ISOtech --> |
| HTML_ENTITIES[8709] = "∅"; //empty set = null set = diameter,U+2205 ISOamso --> |
| HTML_ENTITIES[8711] = "∇"; //nabla = backward difference,U+2207 ISOtech --> |
| HTML_ENTITIES[8712] = "∈"; //element of, U+2208 ISOtech --> |
| HTML_ENTITIES[8713] = "∉"; //not an element of, U+2209 ISOtech --> |
| HTML_ENTITIES[8715] = "∋"; //contains as member, U+220B ISOtech --> |
| // <!-- should there be a more memorable name than 'ni'? --> |
| HTML_ENTITIES[8719] = "∏"; //n-ary product = product sign,U+220F ISOamsb --> |
| // <!-- prod is NOT the same character as U+03A0 'greek capital letter pi' though the same glyph might be used for both --> |
| HTML_ENTITIES[8721] = "∑"; //n-ary summation, U+2211 ISOamsb --> |
| // <!-- sum is NOT the same character as U+03A3 'greek capital letter sigma' though the same glyph might be used for both --> |
| HTML_ENTITIES[8722] = "−"; //minus sign, U+2212 ISOtech --> |
| HTML_ENTITIES[8727] = "∗"; //asterisk operator, U+2217 ISOtech --> |
| HTML_ENTITIES[8730] = "√"; //square root = radical sign,U+221A ISOtech --> |
| HTML_ENTITIES[8733] = "∝"; //proportional to, U+221D ISOtech --> |
| HTML_ENTITIES[8734] = "∞"; //infinity, U+221E ISOtech --> |
| HTML_ENTITIES[8736] = "∠"; //angle, U+2220 ISOamso --> |
| HTML_ENTITIES[8743] = "∧"; //logical and = wedge, U+2227 ISOtech --> |
| HTML_ENTITIES[8744] = "∨"; //logical or = vee, U+2228 ISOtech --> |
| HTML_ENTITIES[8745] = "∩"; //intersection = cap, U+2229 ISOtech --> |
| HTML_ENTITIES[8746] = "∪"; //union = cup, U+222A ISOtech --> |
| HTML_ENTITIES[8747] = "∫"; //integral, U+222B ISOtech --> |
| HTML_ENTITIES[8756] = "∴"; //therefore, U+2234 ISOtech --> |
| HTML_ENTITIES[8764] = "∼"; //tilde operator = varies with = similar to,U+223C ISOtech --> |
| // <!-- tilde operator is NOT the same character as the tilde, U+007E,although the same glyph might be used to represent both --> |
| HTML_ENTITIES[8773] = "≅"; //approximately equal to, U+2245 ISOtech --> |
| HTML_ENTITIES[8776] = "≈"; //almost equal to = asymptotic to,U+2248 ISOamsr --> |
| HTML_ENTITIES[8800] = "≠"; //not equal to, U+2260 ISOtech --> |
| HTML_ENTITIES[8801] = "≡"; //identical to, U+2261 ISOtech --> |
| HTML_ENTITIES[8804] = "≤"; //less-than or equal to, U+2264 ISOtech --> |
| HTML_ENTITIES[8805] = "≥"; //greater-than or equal to,U+2265 ISOtech --> |
| HTML_ENTITIES[8834] = "⊂"; //subset of, U+2282 ISOtech --> |
| HTML_ENTITIES[8835] = "⊃"; //superset of, U+2283 ISOtech --> |
| // <!-- note that nsup, 'not a superset of, U+2283' is not covered by the Symbol font encoding and is not included. Should it be, for symmetry?It is in ISOamsn --> <!ENTITY nsub"; 8836 //not a subset of, U+2284 ISOamsn --> |
| HTML_ENTITIES[8838] = "⊆"; //subset of or equal to, U+2286 ISOtech --> |
| HTML_ENTITIES[8839] = "⊇"; //superset of or equal to,U+2287 ISOtech --> |
| HTML_ENTITIES[8853] = "⊕"; //circled plus = direct sum,U+2295 ISOamsb --> |
| HTML_ENTITIES[8855] = "⊗"; //circled times = vector product,U+2297 ISOamsb --> |
| HTML_ENTITIES[8869] = "⊥"; //up tack = orthogonal to = perpendicular,U+22A5 ISOtech --> |
| HTML_ENTITIES[8901] = "⋅"; //dot operator, U+22C5 ISOamsb --> |
| // <!-- dot operator is NOT the same character as U+00B7 middle dot --> |
| // <!-- Miscellaneous Technical --> |
| HTML_ENTITIES[8968] = "⌈"; //left ceiling = apl upstile,U+2308 ISOamsc --> |
| HTML_ENTITIES[8969] = "⌉"; //right ceiling, U+2309 ISOamsc --> |
| HTML_ENTITIES[8970] = "⌊"; //left floor = apl downstile,U+230A ISOamsc --> |
| HTML_ENTITIES[8971] = "⌋"; //right floor, U+230B ISOamsc --> |
| HTML_ENTITIES[9001] = "⟨"; //left-pointing angle bracket = bra,U+2329 ISOtech --> |
| // <!-- lang is NOT the same character as U+003C 'less than' or U+2039 'single left-pointing angle quotation mark' --> |
| HTML_ENTITIES[9002] = "⟩"; //right-pointing angle bracket = ket,U+232A ISOtech --> |
| // <!-- rang is NOT the same character as U+003E 'greater than' or U+203A 'single right-pointing angle quotation mark' --> |
| // <!-- Geometric Shapes --> |
| HTML_ENTITIES[9674] = "◊"; //lozenge, U+25CA ISOpub --> |
| // <!-- Miscellaneous Symbols --> |
| HTML_ENTITIES[9824] = "♠"; //black spade suit, U+2660 ISOpub --> |
| // <!-- black here seems to mean filled as opposed to hollow --> |
| HTML_ENTITIES[9827] = "♣"; //black club suit = shamrock,U+2663 ISOpub --> |
| HTML_ENTITIES[9829] = "♥"; //black heart suit = valentine,U+2665 ISOpub --> |
| HTML_ENTITIES[9830] = "♦"; //black diamond suit, U+2666 ISOpub --> |
| // <!-- Latin Extended-A --> |
| HTML_ENTITIES[338] = "Œ"; // -- latin capital ligature OE,U+0152 ISOlat2 --> |
| HTML_ENTITIES[339] = "œ"; // -- latin small ligature oe, U+0153 ISOlat2 --> |
| // <!-- ligature is a misnomer, this is a separate character in some languages --> |
| HTML_ENTITIES[352] = "Š"; // -- latin capital letter S with caron,U+0160 ISOlat2 --> |
| HTML_ENTITIES[353] = "š"; // -- latin small letter s with caron,U+0161 ISOlat2 --> |
| HTML_ENTITIES[376] = "Ÿ"; // -- latin capital letter Y with diaeresis,U+0178 ISOlat2 --> |
| // <!-- Spacing Modifier Letters --> |
| HTML_ENTITIES[710] = "ˆ"; // -- modifier letter circumflex accent,U+02C6 ISOpub --> |
| HTML_ENTITIES[732] = "˜"; //small tilde, U+02DC ISOdia --> |
| // <!-- General Punctuation --> |
| HTML_ENTITIES[8194] = " "; //en space, U+2002 ISOpub --> |
| HTML_ENTITIES[8195] = " "; //em space, U+2003 ISOpub --> |
| HTML_ENTITIES[8201] = " "; //thin space, U+2009 ISOpub --> |
| HTML_ENTITIES[8204] = "‌"; //zero width non-joiner,U+200C NEW RFC 2070 --> |
| HTML_ENTITIES[8205] = "‍"; //zero width joiner, U+200D NEW RFC 2070 --> |
| HTML_ENTITIES[8206] = "‎"; //left-to-right mark, U+200E NEW RFC 2070 --> |
| HTML_ENTITIES[8207] = "‏"; //right-to-left mark, U+200F NEW RFC 2070 --> |
| HTML_ENTITIES[8211] = "–"; //en dash, U+2013 ISOpub --> |
| HTML_ENTITIES[8212] = "—"; //em dash, U+2014 ISOpub --> |
| HTML_ENTITIES[8216] = "‘"; //left single quotation mark,U+2018 ISOnum --> |
| HTML_ENTITIES[8217] = "’"; //right single quotation mark,U+2019 ISOnum --> |
| HTML_ENTITIES[8218] = "‚"; //single low-9 quotation mark, U+201A NEW --> |
| HTML_ENTITIES[8220] = "“"; //left double quotation mark,U+201C ISOnum --> |
| HTML_ENTITIES[8221] = "”"; //right double quotation mark,U+201D ISOnum --> |
| HTML_ENTITIES[8222] = "„"; //double low-9 quotation mark, U+201E NEW --> |
| HTML_ENTITIES[8224] = "†"; //dagger, U+2020 ISOpub --> |
| HTML_ENTITIES[8225] = "‡"; //double dagger, U+2021 ISOpub --> |
| HTML_ENTITIES[8240] = "‰"; //per mille sign, U+2030 ISOtech --> |
| HTML_ENTITIES[8249] = "‹"; //single left-pointing angle quotation mark,U+2039 ISO proposed --> |
| // <!-- lsaquo is proposed but not yet ISO standardized --> |
| HTML_ENTITIES[8250] = "›"; //single right-pointing angle quotation mark,U+203A ISO proposed --> |
| // <!-- rsaquo is proposed but not yet ISO standardized --> |
| HTML_ENTITIES[8364] = "€"; // -- euro sign, U+20AC NEW --> |
| }; |
| |
| /** |
| * The array of escaped XML character values, indexed on char value. |
| * <p/> |
| * XML entities values were derived from Jakarta Commons Lang |
| * <tt>org.apache.commons.lang.Entities</tt> class. |
| */ |
| private static final String[] XML_ENTITIES = new String[63]; |
| |
| static { |
| XML_ENTITIES[34] = """; // " - double-quote |
| XML_ENTITIES[38] = "&"; // & - ampersand |
| XML_ENTITIES[39] = "'"; // ' - quote |
| XML_ENTITIES[60] = "<"; // < - less-than |
| XML_ENTITIES[62] = ">"; // > - greater-than |
| }; |
| |
| // --------------------------------------------------------- Public Methods |
| |
| /** |
| * Perform an auto post redirect to the specified target using the given |
| * response. If the params Map is defined then the form will post these |
| * values as name value pairs. If the compress value is true, this method |
| * will attempt to gzip compress the response content if requesting |
| * browser accepts "gzip" encoding. |
| * <p/> |
| * Once this method has returned you should not attempt to write to the |
| * servlet response. |
| * |
| * @param request the servlet request |
| * @param response the servlet response |
| * @param target the target URL to send the auto post redirect to |
| * @param params the map of parameter values to post |
| * @param compress the flag to specify whether to attempt gzip compression |
| * of the response content |
| */ |
| public static void autoPostRedirect(HttpServletRequest request, |
| HttpServletResponse response, String target, Map<?, ?> params, |
| boolean compress) { |
| |
| Validate.notNull(request, "Null response parameter"); |
| Validate.notNull(response, "Null response parameter"); |
| Validate.notNull(target, "Null target parameter"); |
| |
| HtmlStringBuffer buffer = new HtmlStringBuffer(1024); |
| buffer.append("<html><body onload=\"document.forms[0].submit();\">"); |
| buffer.append("<form name=\"form\" method=\"post\" style=\"{display:none;}\" action=\""); |
| buffer.append(target); |
| buffer.append("\">"); |
| for (Map.Entry<?, ?> entry : params.entrySet()) { |
| buffer.elementStart("textarea"); |
| buffer.appendAttribute("name", entry.getKey()); |
| buffer.elementEnd(); |
| buffer.append(entry.getValue()); |
| buffer.elementEnd("textarea"); |
| } |
| buffer.append("</form></body></html>"); |
| |
| // Determine whether browser will accept gzip compression |
| if (compress) { |
| compress = false; |
| Enumeration<?> e = request.getHeaders("Accept-Encoding"); |
| |
| while (e.hasMoreElements()) { |
| String name = (String) e.nextElement(); |
| if (name.indexOf("gzip") != -1) { |
| compress = true; |
| break; |
| } |
| } |
| } |
| |
| OutputStream os = null; |
| try { |
| response.setContentType("text/html"); |
| |
| // Only compress if flag set, and HTML value is greater than 2K |
| if (compress && buffer.length() > 2024) { |
| response.setHeader("Content-Encoding", "gzip"); |
| |
| ByteArrayOutputStream bos = null; |
| GZIPOutputStream gos = null; |
| try { |
| bos = new ByteArrayOutputStream(); |
| gos = new GZIPOutputStream(bos); |
| gos.write(buffer.toString().getBytes()); |
| |
| } finally { |
| ClickUtils.close(gos); |
| ClickUtils.close(bos); |
| } |
| |
| byte[] byteArray = bos.toByteArray(); |
| response.setContentLength(byteArray.length); |
| |
| os = response.getOutputStream(); |
| IOUtils.write(byteArray, os); |
| |
| } else { |
| response.setContentLength(buffer.length()); |
| |
| os = response.getOutputStream(); |
| os.write(buffer.toString().getBytes()); |
| } |
| |
| } catch (IOException ex) { |
| ClickUtils.getLogService().error(ex.getMessage(), ex); |
| |
| } finally { |
| ClickUtils.close(os); |
| } |
| } |
| |
| /** |
| * A helper method that binds the submitted request value to the Field's |
| * value. Since Field values are only bound during the <tt>"onProcess"</tt> |
| * event, this method can be used to bind a submitted Field value during |
| * the <tt>"onInit"</tt> event, which occurs <b>before</b> the |
| * <tt>"onProcess"</tt> event. |
| * <p/> |
| * This is especially useful for dynamic Form and Page behavior where Field |
| * values are inspected during the <tt>"onInit"</tt> event to add or remove |
| * specific Fields. |
| * <p/> |
| * <b>Please note</b>: this method won't bind disabled fields, unless the |
| * field has an incoming request parameter matching its name. If an incoming |
| * request parameter is present, this method will switch off the Field's |
| * disabled property. |
| * <p/> |
| * This method delegates to |
| * {@link #canBind(org.apache.click.Control, org.apache.click.Context)} |
| * to check if the Field value can be bound. |
| * <p/> |
| * <pre class="prettyprint"> |
| * public void onInit() { |
| * Form form = new Form("form"); |
| * Select select = new Select("select"); |
| * select.setAttribute("onchange", "Click.submit(form, false)"); |
| * |
| * // Bind the select Field request value |
| * ClickUtils.bind(select); |
| * |
| * if (select.getValue() == COMPANY) { |
| * form.add(new TextField("companyName")); |
| * } else { |
| * form.add(new TextField("fullname")); |
| * form.add(new TextField("age")); |
| * } |
| * } </pre> |
| * |
| * @param field the Field to bind |
| */ |
| public static void bind(Field field) { |
| Context context = Context.getThreadLocalContext(); |
| if (canBind(field, context)) { |
| bindField(field, context); |
| } |
| } |
| |
| /** |
| * A helper method that binds the submitted request value to the Link's |
| * value. See {@link #bind(org.apache.click.control.Field)} for a detailed |
| * description. |
| * <p/> |
| * This method delegates to |
| * {@link #canBind(org.apache.click.Control, org.apache.click.Context)} |
| * to check if the Link value can be bound. |
| * |
| * @param link the AbstractLink to bind |
| */ |
| public static void bind(AbstractLink link) { |
| Context context = Context.getThreadLocalContext(); |
| if (canBind(link, context)) { |
| link.bindRequestValue(); |
| } |
| } |
| |
| /** |
| * A helper method that binds the submitted request values of all Fields |
| * and Links inside the given container or child containers. See |
| * {@link #bind(org.apache.click.control.Field)} for a detailed description. |
| * <p/> |
| * This method delegates to |
| * {@link #canBind(org.apache.click.Control, org.apache.click.Context)} |
| * to check if the Container Fields and Links can be bound. |
| * <p/> |
| * Below is an example to bind Form Field's during the onInit event: |
| * |
| * <pre class="prettyprint"> |
| * public void onInit() { |
| * Form form = new Form("form"); |
| * Checkbox commentChk = new Checkbox("comment"); |
| * Select select = new Select("select"); |
| * select.setAttribute("onchange", "Click.submit(form, false)"); |
| * |
| * // Bind all Form Field request values |
| * ClickUtils.bind(form); |
| * |
| * if (select.getValue() == COMPANY) { |
| * form.add(new TextField("companyName")); |
| * } else { |
| * form.add(new TextField("fullname")); |
| * form.add(new TextField("age")); |
| * } |
| * |
| * if (commentChk.isChecked()) { |
| * form.add(new TextArea("feedback")); |
| * } |
| * } </pre> |
| * |
| * @param container the container which Fields and Links to bind |
| */ |
| public static void bind(Container container) { |
| Context context = Context.getThreadLocalContext(); |
| if (canBind(container, context)) { |
| bind(container, context); |
| } |
| } |
| |
| /** |
| * A helper method that binds and validates the Field's submitted request |
| * value. This method will return true if the validation succeeds, false |
| * otherwise. See {@link #bind(org.apache.click.control.Field)} for a |
| * detailed description. |
| * <p/> |
| * This method delegates to |
| * {@link #canBind(org.apache.click.Control, org.apache.click.Context)} |
| * to check if the Field value can be bound and validated. |
| * <p/> |
| * <b>Please note</b>: this method won't bind and validate disabled fields, |
| * unless the field has an incoming request parameter matching its name. |
| * If an incoming request parameter is present, this method will switch off |
| * the Field's disabled property. |
| * <p/> |
| * <pre class="prettyprint"> |
| * public void onInit() { |
| * Form form = new Form("form"); |
| * Select select = new Select("select", true); |
| * select.addOption(Option.EMPTY_OPTION); |
| * |
| * select.setAttribute("onchange", "Click.submit(form, false)"); |
| * |
| * // Bind the Field request value and validate it before continuing |
| * if (ClickUtils.bindAndValidate(select)) { |
| * if (select.getValue() == COMPANY) { |
| * form.add(new TextField("companyName")); |
| * } else { |
| * form.add(new TextField("fullname")); |
| * form.add(new TextField("age")); |
| * } |
| * } |
| * } </pre> |
| * |
| * @param field the Field to bind and validate |
| * @return true if field was bound and valid, or false otherwise |
| */ |
| public static boolean bindAndValidate(Field field) { |
| Context context = Context.getThreadLocalContext(); |
| if (canBind(field, context)) { |
| return bindAndValidate(field, context); |
| } |
| return false; |
| } |
| |
| /** |
| * A helper method that binds and validates the submitted request values |
| * of all Fields and Links inside the given container or child containers. |
| * This method will return true if the validation succeeds, false |
| * otherwise. |
| * <p/> |
| * See {@link #bindAndValidate(org.apache.click.control.Form)} for a |
| * detailed description. |
| * |
| * @param container the container which Fields and Links to bind and |
| * validate |
| * @return true if all Fields are valid, false otherwise |
| */ |
| public static boolean bindAndValidate(Container container) { |
| Context context = Context.getThreadLocalContext(); |
| if (canBind(container, context)) { |
| return bindAndValidate(container, context); |
| } |
| return false; |
| } |
| |
| /** |
| * * A helper method that binds and validates the submitted request values |
| * of all Fields and Links inside the given Form or child containers. Note, |
| * the Form itself is also validated. |
| * <p/> |
| * This method will return true if the validation succeeds, false otherwise. |
| * See {@link #bind(org.apache.click.control.Field)} for a detailed |
| * description. |
| * <p/> |
| * This method delegates to |
| * {@link #canBind(org.apache.click.Control, org.apache.click.Context)} |
| * to check if the Form Fields and Links can be bound and validated. |
| * |
| * <pre class="prettyprint"> |
| * public void onInit() { |
| * Form form = new Form("form"); |
| * Checkbox commentChk = new Checkbox("comment"); |
| * Select select = new Select("select", true); |
| * select.addOption(Option.EMPTY_OPTION); |
| * |
| * select.setAttribute("onchange", "Click.submit(form, false)"); |
| * |
| * // Bind all Form field request values and validate it before continuing |
| * if (ClickUtils.bindAndValidate(form)) { |
| * |
| * if (select.getValue() == COMPANY) { |
| * form.add(new TextField("companyName")); |
| * } else { |
| * form.add(new TextField("fullname")); |
| * form.add(new TextField("age")); |
| * } |
| * |
| * if (commentChk.isChecked()) { |
| * form.add(new TextArea("feedback")); |
| * } |
| * } |
| * } </pre> |
| * |
| * @param form the form which Fields and Links to bind and validate |
| * @return true if the form, it's fields and links was bound and valid, false |
| * otherwise |
| */ |
| public static boolean bindAndValidate(Form form) { |
| Context context = Context.getThreadLocalContext(); |
| if (canBind(form, context)) { |
| return bindAndValidate(form, context); |
| } |
| return false; |
| } |
| |
| /** |
| * Return a new XML Document for the given input stream. |
| * |
| * @param inputStream the input stream |
| * @return new XML Document |
| * @throws RuntimeException if a parsing error occurs |
| */ |
| public static Document buildDocument(InputStream inputStream) { |
| return buildDocument(inputStream, null); |
| } |
| |
| /** |
| * Return a new XML Document for the given input stream and XML entity |
| * resolver. |
| * |
| * @param inputStream the input stream |
| * @param entityResolver the XML entity resolver |
| * @return new XML Document |
| * @throws RuntimeException if a parsing error occurs |
| */ |
| public static Document buildDocument(InputStream inputStream, |
| EntityResolver entityResolver) { |
| try { |
| DocumentBuilderFactory factory = |
| DocumentBuilderFactory.newInstance(); |
| |
| DocumentBuilder builder = factory.newDocumentBuilder(); |
| |
| if (entityResolver != null) { |
| builder.setEntityResolver(entityResolver); |
| } |
| |
| return builder.parse(inputStream); |
| |
| } catch (Exception ex) { |
| throw new RuntimeException("Error parsing XML", ex); |
| } |
| } |
| |
| /** |
| * Return true if the given control's request value can be bound, false |
| * otherwise. |
| * <p/> |
| * The following algorithm is used to determine if the Control can be |
| * bound to a request value or not. |
| * <ul> |
| * <li>return false if the request is a forward. |
| * See {@link org.apache.click.Context#isForward()}</li> |
| * <li>return true if the request is an Ajax request. |
| * See {@link org.apache.click.Context#isAjaxRequest()}</li> |
| * <li>return true if the control has no parent Form</li> |
| * <li>return true if the control's parent Form was submitted, false otherwise. |
| * See {@link org.apache.click.control.Form#isFormSubmission()}</li> |
| * </ul> |
| * |
| * @param control the control to check if it can be bound or not |
| * @param context the request context |
| * @return true if the given control request value be bound, false otherwise |
| */ |
| public static boolean canBind(Control control, Context context) { |
| |
| if (context.isForward()) { |
| return false; |
| } |
| |
| // This can cause issue with two fields inside a form with the same name. |
| if (context.isAjaxRequest()) { |
| return true; |
| } |
| |
| Form form = ContainerUtils.findForm(control); |
| if (form == null && control instanceof Form) { |
| form = (Form) control; |
| } |
| if (form != null) { |
| return form.isFormSubmission(); |
| } |
| return true; |
| } |
| |
| /** |
| * Returns the <code>Class</code> object associated with the class or |
| * interface with the given string name, using the current Thread context |
| * class loader. |
| * |
| * @param classname the name of the class to load |
| * @return the <tt>Class</tt> object |
| * @throws ClassNotFoundException if the class cannot be located |
| */ |
| public static Class classForName(String classname) |
| throws ClassNotFoundException { |
| ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); |
| return Class.forName(classname, true, classLoader); |
| } |
| |
| /** |
| * Close the given closeable (Reader, Writer, Stream) and ignore any |
| * exceptions thrown. |
| * |
| * @param closeable the closeable (Reader, Writer, Stream) to close. |
| */ |
| public static void close(Closeable closeable) { |
| if (closeable != null) { |
| try { |
| closeable.close(); |
| } catch (IOException ioe) { |
| // Ignore |
| } |
| } |
| } |
| |
| /** |
| * Creates a template model of key/value pairs which can be used by template |
| * engines such as Velocity and Freemarker. |
| * <p/> |
| * The following objects will be added to the model: |
| * <ul> |
| * <li>the Page {@link org.apache.click.Page#model model} Map key/value |
| * pairs |
| * </li> |
| * <li>context - the Servlet context path, e.g. <span class="">/mycorp</span> |
| * </li> |
| * <li>format - the Page {@link Format} object for formatting the display |
| * of objects. |
| * </li> |
| * <li>messages - the {@link MessagesMap} adaptor for the |
| * {@link org.apache.click.Page#getMessages()} method. |
| * </li> |
| * <li>path - the {@link org.apache.click.Page#path path} of the <tt>page</tt> |
| * template. |
| * </li> |
| * <li>request - the page {@link javax.servlet.http.HttpServletRequest} |
| * object. |
| * </li> |
| * <li>response - the page {@link javax.servlet.http.HttpServletResponse} |
| * object. |
| * </li> |
| * <li>session - the {@link SessionMap} adaptor for the users |
| * {@link javax.servlet.http.HttpSession}. |
| * </li> |
| * </ul> |
| * |
| * @param page the page to populate the template model from |
| * @param context the request context |
| * @return a template model as a map |
| */ |
| public static Map<String, Object> createTemplateModel(final Page page, Context context) { |
| |
| ConfigService configService = ClickUtils.getConfigService(context.getServletContext()); |
| LogService logger = configService.getLogService(); |
| |
| final Map<String, Object> model = new HashMap<String, Object>(page.getModel()); |
| |
| final HttpServletRequest request = context.getRequest(); |
| |
| Object pop = model.put("request", request); |
| if (pop != null && !page.isStateful()) { |
| String msg = page.getClass().getName() + " on " + page.getPath() |
| + " model contains an object keyed with reserved " |
| + "name \"request\". The page model object " |
| + pop + " has been replaced with the request object"; |
| logger.warn(msg); |
| } |
| |
| pop = model.put("response", context.getResponse()); |
| if (pop != null && !page.isStateful()) { |
| String msg = page.getClass().getName() + " on " + page.getPath() |
| + " model contains an object keyed with reserved " |
| + "name \"response\". The page model object " |
| + pop + " has been replaced with the response object"; |
| logger.warn(msg); |
| } |
| |
| SessionMap sessionMap = new SessionMap(request.getSession(false)); |
| pop = model.put("session", sessionMap); |
| if (pop != null && !page.isStateful()) { |
| String msg = page.getClass().getName() + " on " + page.getPath() |
| + " model contains an object keyed with reserved " |
| + "name \"session\". The page model object " |
| + pop + " has been replaced with the request " |
| + " session"; |
| logger.warn(msg); |
| } |
| |
| pop = model.put("context", request.getContextPath()); |
| if (pop != null && !page.isStateful()) { |
| String msg = page.getClass().getName() + " on " + page.getPath() |
| + " model contains an object keyed with reserved " |
| + "name \"context\". The page model object " |
| + pop + " has been replaced with the request " |
| + " context path"; |
| logger.warn(msg); |
| } |
| |
| Format format = page.getFormat(); |
| if (format != null) { |
| pop = model.put("format", format); |
| if (pop != null && !page.isStateful()) { |
| String msg = page.getClass().getName() + " on " |
| + page.getPath() |
| + " model contains an object keyed with reserved " |
| + "name \"format\". The page model object " + pop |
| + " has been replaced with the format object"; |
| logger.warn(msg); |
| } |
| } |
| |
| String path = page.getPath(); |
| if (path != null) { |
| pop = model.put("path", path); |
| if (pop != null && !page.isStateful()) { |
| String msg = page.getClass().getName() + " on " |
| + page.getPath() |
| + " model contains an object keyed with reserved " |
| + "name \"path\". The page model object " + pop |
| + " has been replaced with the page path"; |
| logger.warn(msg); |
| } |
| } |
| |
| pop = model.put("messages", page.getMessages()); |
| if (pop != null && !page.isStateful()) { |
| String msg = page.getClass().getName() + " on " + page.getPath() |
| + " model contains an object keyed with reserved " |
| + "name \"messages\". The page model object " |
| + pop + " has been replaced with the request " |
| + " messages"; |
| logger.warn(msg); |
| } |
| |
| return model; |
| } |
| |
| /** |
| * Invalidate the specified cookie and delete it from the response object. |
| * <p/> |
| * This method was derived from Atlassian <tt>CookieUtils</tt> method of |
| * the same name, release under the BSD License. |
| * |
| * @param request the servlet request |
| * @param response the servlet response |
| * @param cookieName The name of the cookie you want to delete |
| * @param path of the path the cookie you want to delete |
| */ |
| public static void invalidateCookie(HttpServletRequest request, |
| HttpServletResponse response, String cookieName, String path) { |
| |
| setCookie(request, response, cookieName, null, 0, path); |
| } |
| |
| /** |
| * Return true is this is an Ajax request, false otherwise. |
| * <p/> |
| * An Ajax request is identified by the presence of the request <tt>header</tt> |
| * or request <tt>parameter</tt>: "<tt>X-Requested-With</tt>". |
| * "<tt>X-Requested-With</tt>" is the de-facto standard identifier used by |
| * Ajax libraries. |
| * <p/> |
| * <b>Note:</b> incoming requests that contains a request <tt>parameter</tt> |
| * "<tt>X-Requested-With</tt>" will result in this method returning true, even |
| * though the request itself was not initiated through a <tt>XmlHttpRequest</tt> |
| * object. This allows one to programmatically enable Ajax requests. A common |
| * use case for this feature is when uploading files through an IFrame element. |
| * By specifying "<tt>X-Requested-With</tt>" as a request parameter the IFrame |
| * request will be handled like a normal Ajax request. |
| * |
| * @param request the servlet request |
| * @return true if this is an Ajax request, false otherwise |
| */ |
| public static boolean isAjaxRequest(HttpServletRequest request) { |
| return request.getHeader(X_REQUESTED_WITH) != null |
| || request.getParameter(X_REQUESTED_WITH) != null; |
| } |
| |
| /** |
| * Return true if the request is a multi-part content type POST request. |
| * |
| * @param request the page servlet request |
| * @return true if the request is a multi-part content type POST request |
| */ |
| public static boolean isMultipartRequest(HttpServletRequest request) { |
| return ServletFileUpload.isMultipartContent(request); |
| } |
| |
| /** |
| * Invalidate the specified cookie and delete it from the response object. Deletes only cookies mapped |
| * against the root "/" path. Otherwise use |
| * {@link #invalidateCookie(HttpServletRequest, HttpServletResponse, String, String)} |
| * <p/> |
| * This method was derived from Atlassian <tt>CookieUtils</tt> method of |
| * the same name, release under the BSD License. |
| * |
| * @see #invalidateCookie(HttpServletRequest, HttpServletResponse, String, String) |
| * |
| * @param request the servlet request |
| * @param response the servlet response |
| * @param cookieName The name of the cookie you want to delete. |
| */ |
| public static void invalidateCookie(HttpServletRequest request, |
| HttpServletResponse response, String cookieName) { |
| |
| invalidateCookie(request, response, cookieName, "/"); |
| } |
| |
| /** |
| * Return a resource bundle using the specified base name. |
| * |
| * @param baseName the base name of the resource bundle, a fully qualified class name |
| * @return a resource bundle for the given base name |
| * @throws MissingResourceException if no resource bundle for the specified base name can be found |
| */ |
| public static ResourceBundle getBundle(String baseName) { |
| return getBundle(baseName, Locale.getDefault()); |
| } |
| |
| /** |
| * Return a resource bundle using the specified base name and locale. |
| * |
| * @param baseName the base name of the resource bundle, a fully qualified class name |
| * @param locale the locale for which a resource bundle is desired |
| * @return a resource bundle for the given base name and locale |
| * @throws MissingResourceException if no resource bundle for the specified base name can be found |
| */ |
| public static ResourceBundle getBundle(String baseName, Locale locale) { |
| ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); |
| return ResourceBundle.getBundle(baseName, locale, classLoader); |
| } |
| |
| /** |
| * Return the first XML child Element for the given parent Element and child |
| * Element name. |
| * |
| * @param parent the parent element to get the child from |
| * @param name the name of the child element |
| * @return the first child element for the given name and parent |
| */ |
| public static Element getChild(Element parent, String name) { |
| NodeList nodeList = parent.getChildNodes(); |
| for (int i = 0; i < nodeList.getLength(); i++) { |
| Node node = nodeList.item(i); |
| if (node instanceof Element) { |
| if (node.getNodeName().equals(name)) { |
| return (Element) node; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Return the list of XML child Element elements with the given name from |
| * the given parent Element. |
| * |
| * @param parent the parent element to get the child from |
| * @param name the name of the child element |
| * @return the list of XML child elements for the given name |
| */ |
| public static List<Element> getChildren(Element parent, String name) { |
| List<Element> list = new ArrayList<Element>(); |
| NodeList nodeList = parent.getChildNodes(); |
| for (int i = 0; i < nodeList.getLength(); i++) { |
| Node node = nodeList.item(i); |
| if (node instanceof Element) { |
| if (node.getNodeName().equals(name)) { |
| list.add((Element) node); |
| } |
| } |
| } |
| return list; |
| } |
| |
| /** |
| * Return the InputStream for the Click configuration file <tt>click.xml</tt>. |
| * This method will first lookup the <tt>click.xml</tt> under the |
| * applications <tt>WEB-INF</tt> directory, and then if not found it will |
| * attempt to find the configuration file on the classpath root. |
| * |
| * @param servletContext the servlet context to obtain the Click configuration |
| * from |
| * @return the InputStream for the Click configuration file |
| * @throws RuntimeException if the resource could not be found |
| */ |
| public static InputStream getClickConfig(ServletContext servletContext) { |
| InputStream inputStream = |
| servletContext.getResourceAsStream(DEFAULT_APP_CONFIG); |
| |
| if (inputStream == null) { |
| inputStream = ClickUtils.getResourceAsStream("/click.xml", ClickUtils.class); |
| if (inputStream == null) { |
| String msg = |
| "could not find click app configuration file: " |
| + DEFAULT_APP_CONFIG + " or click.xml on classpath"; |
| throw new RuntimeException(msg); |
| } |
| } |
| |
| return inputStream; |
| } |
| |
| /** |
| * Return the application configuration service instance from the given |
| * servlet context. |
| * |
| * @param servletContext the servlet context to get the config service instance |
| * @return the application config service instance |
| */ |
| public static ConfigService getConfigService(ServletContext servletContext) { |
| ConfigService configService = (ConfigService) |
| servletContext.getAttribute(ConfigService.CONTEXT_NAME); |
| |
| if (configService != null) { |
| return configService; |
| |
| } else { |
| String msg = |
| "could not find ConfigService in the ServletContext under the" |
| + " name '" + ConfigService.CONTEXT_NAME + "'.\nThis can occur" |
| + " if ClickUtils.getConfigService() is called before" |
| + " ClickServlet is initialized by the servlet container.\n" |
| + "To fix ensure that ClickServlet is loaded at startup by" |
| + " editing your web.xml and setting the load-on-startup to 0:\n\n" |
| + " <servlet>\n" |
| + " <servlet-name>ClickServlet</servlet-name>\n" |
| + " <servlet-class>org.apache.click.ClickServlet</servlet-class>\n" |
| + " <load-on-startup>0</load-on-startup>\n" |
| + " </servlet>\n"; |
| |
| throw new RuntimeException(msg); |
| } |
| } |
| |
| /** |
| * Return the application configuration service instance from the thread |
| * local context. |
| * |
| * @return the application config service instance |
| */ |
| public static ConfigService getConfigService() { |
| ServletContext servletContext = |
| Context.getThreadLocalContext().getServletContext(); |
| |
| return getConfigService(servletContext); |
| } |
| |
| /** |
| * Returns the specified Cookie object, or null if the cookie does not exist. |
| * <p/> |
| * This method was derived from Atlassian <tt>CookieUtils</tt> method of |
| * the same name, release under the BSD License. |
| * |
| * @param request the servlet request |
| * @param name the name of the cookie |
| * @return the Cookie object if it exists, otherwise null |
| */ |
| public static Cookie getCookie(HttpServletRequest request, String name) { |
| Cookie cookies[] = request.getCookies(); |
| |
| if (cookies == null || name == null || name.length() == 0) { |
| return null; |
| } |
| |
| //Otherwise, we have to do a linear scan for the cookie. |
| for (Cookie cookie : cookies) { |
| if (cookie.getName().equals(name)) { |
| return cookie; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Sets the given cookie values in the servlet response. |
| * <p/> |
| * This will also put the cookie in a list of cookies to send with this request's response |
| * (so that in case of a redirect occurring down the chain, the first filter |
| * will always try to set this cookie again) |
| * <p/> |
| * The cookie secure flag is set if the request is secure. |
| * <p/> |
| * This method was derived from Atlassian <tt>CookieUtils</tt> method of |
| * the same name, release under the BSD License. |
| * |
| * @param request the servlet request |
| * @param response the servlet response |
| * @param name the cookie name |
| * @param value the cookie value |
| * @param maxAge the maximum age of the cookie in seconds. A negative |
| * value will expire the cookie at the end of the session, while 0 will delete |
| * the cookie. |
| * @param path the cookie path |
| * @return the Cookie object created and set in the response |
| */ |
| public static Cookie setCookie(HttpServletRequest request, HttpServletResponse response, |
| String name, String value, int maxAge, String path) { |
| |
| Cookie cookie = new Cookie(name, value); |
| cookie.setMaxAge(maxAge); |
| cookie.setPath(path); |
| cookie.setSecure(request.isSecure()); |
| response.addCookie(cookie); |
| |
| return cookie; |
| } |
| |
| /** |
| * Returns the value of the specified cookie as a String. If the cookie |
| * does not exist, the method returns null. |
| * <p/> |
| * This method was derived from Atlassian <tt>CookieUtils</tt> method of |
| * the same name, release under the BSD License. |
| * |
| * @param request the servlet request |
| * @param name the name of the cookie |
| * @return the value of the cookie, or null if the cookie does not exist. |
| */ |
| public static String getCookieValue(HttpServletRequest request, String name) { |
| Cookie cookie = getCookie(request, name); |
| |
| if (cookie != null) { |
| return cookie.getValue(); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Return the Click Framework version string. |
| * |
| * @return the Click Framework version string |
| */ |
| public static String getClickVersion() { |
| ResourceBundle bundle = getBundle("click-control"); |
| return bundle.getString("click-version"); |
| } |
| |
| /** |
| * Return the web application version string. |
| * |
| * @return the web application version string |
| */ |
| public static String getApplicationVersion() { |
| return applicationVersion; |
| } |
| |
| /** |
| * Set the web application version string. |
| * |
| * @param applicationVersion the web application version string |
| */ |
| public static void setApplicationVersion(String applicationVersion) { |
| ClickUtils.applicationVersion = applicationVersion; |
| cachedApplicationVersionIndicator = null; |
| } |
| |
| /** |
| * Return Click's version indicator for static web resources |
| * (eg css, js and image files) if resource versioning is active, |
| * otherwise this method will return an empty string. |
| * <p/> |
| * Click's resource versioning becomes active under the following |
| * conditions: |
| * <ul> |
| * <li>the {@link #ENABLE_RESOURCE_VERSION} request attribute must be set |
| * to <tt>true</tt></li> |
| * <li>the application mode must be either "production" or "profile"</li> |
| * </ul> |
| * |
| * The version indicator is based on the current Click release version. |
| * For example when using Click 1.4 this method will return the string |
| * <tt>"_1.4"</tt>. |
| * |
| * @param context the request context |
| * @return a version indicator for web resources |
| */ |
| public static String getResourceVersionIndicator(Context context) { |
| if (cachedResourceVersionIndicator != null) { |
| return cachedResourceVersionIndicator; |
| } |
| |
| ConfigService configService = getConfigService(context.getServletContext()); |
| |
| boolean isProductionModes = configService.isProductionMode() |
| || configService.isProfileMode(); |
| |
| if (isProductionModes |
| && isEnableResourceVersion(context)) { |
| |
| cachedResourceVersionIndicator = RESOURCE_VERSION_INDICATOR; |
| return cachedResourceVersionIndicator; |
| |
| } else { |
| return ""; |
| } |
| } |
| |
| /** |
| * If resource versioning is active this method will return the |
| * application version indicator for static web resources |
| * (eg JavaScript and Css) otherwise this method will return an empty string. |
| * <p/> |
| * Application resource versioning becomes active under the following |
| * conditions: |
| * <ul> |
| * <li>the {@link #ENABLE_RESOURCE_VERSION} request attribute must be set |
| * to <tt>true</tt></li> |
| * <li>the application mode must be either "production" or "profile"</li> |
| * </ul> |
| * |
| * The version indicator is based on the application version. |
| * For example if the application version is 1.2 this method will |
| * return the string <tt>"_1.2"</tt>. |
| * <p/> |
| * The application version can be set through the static method |
| * {@link #setApplicationVersion(java.lang.String)}. |
| * |
| * @return an application version indicator for web resources |
| */ |
| public static String getApplicationResourceVersionIndicator() { |
| // Return the cached version first |
| if (cachedApplicationVersionIndicator != null) { |
| return cachedApplicationVersionIndicator; |
| } |
| |
| // Check if the Context has been set |
| if (Context.hasThreadLocalContext()) { |
| |
| Context context = Context.getThreadLocalContext(); |
| ConfigService configService = ClickUtils.getConfigService(context.getServletContext()); |
| |
| boolean isProductionModes = configService.isProductionMode() |
| || configService.isProfileMode(); |
| |
| if (isProductionModes && ClickUtils.isEnableResourceVersion(context)) { |
| String version = getApplicationVersion(); |
| if (StringUtils.isNotBlank(version)) { |
| cachedApplicationVersionIndicator = VERSION_INDICATOR_SEP |
| + version; |
| return cachedApplicationVersionIndicator; |
| } |
| } |
| } |
| return ""; |
| } |
| |
| /** |
| * Return the given control CSS selector or null if no selector can be found. |
| * <p/> |
| * <b>Please note:</b> it is highly recommended to set a control's ID |
| * attribute when dealing with Ajax requests. |
| * <p/> |
| * The CSS selector is calculated as follows: |
| * <ol> |
| * <li>if control.getId() is set, prepend it with a '#' char |
| * and return the value. An example selector will be: <tt>#field-id</tt> |
| * </li> |
| * |
| * <li>if control.getName() is set do the following: |
| * <ol> |
| * <li>if the control is of type {@link org.apache.click.control.ActionLink}, |
| * it's "<tt>class</tt>" attribute selector will be returned. For example: |
| * <tt>a[class=red]</tt>. <b>Please note:</b> if the link class attribute is |
| * not set, the class attribute will be set to its name, prefixed with |
| * a dash, '-'. For example: <tt>a[class=-my-link]</tt>. |
| * </li> |
| * |
| * <li>if the control is not an ActionLink, it is assumed the control |
| * will render its "<tt>name</tt>" attribute and the name attribute |
| * selector will be returned. For example: <tt>input[name=my-button]</tt>. |
| * </li> |
| * </ol> |
| * </li> |
| * |
| * <li>otherwise return null. |
| * </li> |
| * </ol> |
| * |
| * @param control the control which CSS selector to return |
| * @return the control CSS selector or null if no selector can be found |
| * @throws IllegalArgumentException if control is null |
| */ |
| public static String getCssSelector(Control control) { |
| if (control == null) { |
| throw new IllegalArgumentException("Control cannot be null"); |
| } |
| |
| String id = control.getId(); |
| String name = control.getName(); |
| String cssSelector = null; |
| |
| if (StringUtils.isNotBlank(id)) { |
| cssSelector = '#' + id; |
| } else if (StringUtils.isNotBlank(name)) { |
| String tag = null; |
| |
| // Try and create a more specific selector by retrieving the |
| // control's tag |
| if (control instanceof AbstractControl) { |
| tag = StringUtils.defaultString(((AbstractControl) control).getTag()); |
| } |
| |
| HtmlStringBuffer buffer = new HtmlStringBuffer(20); |
| |
| // Handle ActionLink (perhaps other link controls too?) differently |
| // as it doesn't render the "name" attribute. The "name" attribute |
| // is used by links for bookmarking purposes. Instead set the class |
| // attribute to the link's name and use that as the selector. |
| if (control instanceof ActionLink) { |
| ActionLink link = (ActionLink) control; |
| if (!link.hasAttribute("class")) { |
| link.setAttribute("class", '-' + name); |
| } |
| buffer.append(tag).append("[class*="); |
| buffer.append(link.getAttribute("class")).append("]"); |
| |
| } else { |
| buffer.append(tag).append("[name="); |
| buffer.append(name).append("]"); |
| } |
| cssSelector = buffer.toString(); |
| } |
| return cssSelector; |
| } |
| |
| /** |
| * Populate the given object's attributes with the Form's field values. |
| * <p/> |
| * The specified Object can either be a POJO (plain old java object) or |
| * a {@link java.util.Map}. If a POJO is specified, its attributes are |
| * populated from matching form fields. If a map is specified, its |
| * key/value pairs are populated from matching form fields. |
| * |
| * @param form the Form to obtain field values from |
| * @param object the object to populate with field values |
| * @param debug log debug statements when populating the object |
| */ |
| public static void copyFormToObject(Form form, Object object, |
| boolean debug) { |
| |
| ContainerUtils.copyContainerToObject(form, object); |
| } |
| |
| /** |
| * Populate the given Form field values with the object's attributes. |
| * <p/> |
| * The specified Object can either be a POJO (plain old java object) or |
| * a {@link java.util.Map}. If a POJO is specified, its attributes are |
| * copied to matching form fields. If a map is specified, its key/value |
| * pairs are copied to matching form fields. |
| * |
| * @param object the object to obtain attribute values from |
| * @param form the Form to populate |
| * @param debug log debug statements when populating the form |
| */ |
| public static void copyObjectToForm(Object object, Form form, |
| boolean debug) { |
| |
| ContainerUtils.copyObjectToContainer(object, form); |
| } |
| |
| /** |
| * Deploy the specified classpath resource to the given target directory |
| * under the web application root directory. |
| * <p/> |
| * This method will <b>not</b> override any existing resources found in the |
| * target directory. |
| * <p/> |
| * If an IOException or SecurityException occurs this method will log a |
| * warning message. |
| * |
| * @param servletContext the web applications servlet context |
| * @param resource the classpath resource name |
| * @param targetDir the target directory to deploy the resource to |
| */ |
| public static void deployFile(ServletContext servletContext, |
| String resource, String targetDir) { |
| |
| if (servletContext == null) { |
| throw new IllegalArgumentException("Null servletContext parameter"); |
| } |
| |
| if (StringUtils.isBlank(resource)) { |
| String msg = "Null resource parameter not defined"; |
| throw new IllegalArgumentException(msg); |
| } |
| |
| String realTargetDir = servletContext.getRealPath("/") + File.separator; |
| |
| if (StringUtils.isNotBlank(targetDir)) { |
| realTargetDir = realTargetDir + targetDir; |
| } |
| |
| |
| LogService logger = getConfigService(servletContext).getLogService(); |
| |
| try { |
| |
| // Create files deployment directory |
| File directory = new File(realTargetDir); |
| if (!directory.exists()) { |
| if (!directory.mkdirs()) { |
| String msg = |
| "could not create deployment directory: " + directory; |
| throw new IOException(msg); |
| } |
| } |
| |
| String destination = resource; |
| int index = resource.lastIndexOf('/'); |
| if (index != -1) { |
| destination = resource.substring(index + 1); |
| } |
| |
| destination = realTargetDir + File.separator + destination; |
| |
| File destinationFile = new File(destination); |
| |
| if (!destinationFile.exists()) { |
| InputStream inputStream = |
| getResourceAsStream(resource, ClickUtils.class); |
| |
| if (inputStream != null) { |
| FileOutputStream fos = null; |
| try { |
| fos = new FileOutputStream(destinationFile); |
| byte[] buffer = new byte[1024]; |
| while (true) { |
| int length = inputStream.read(buffer); |
| if (length < 0) { |
| break; |
| } |
| fos.write(buffer, 0, length); |
| } |
| |
| if (logger.isTraceEnabled()) { |
| int lastIndex = |
| destination.lastIndexOf(File.separatorChar); |
| if (lastIndex != -1) { |
| destination = |
| destination.substring(lastIndex + 1); |
| } |
| String msg = |
| "deployed " + targetDir + "/" + destination; |
| logger.trace(msg); |
| } |
| |
| } finally { |
| close(fos); |
| close(inputStream); |
| } |
| } else { |
| String msg = |
| "could not locate classpath resource: " + resource; |
| throw new IOException(msg); |
| } |
| } |
| |
| } catch (IOException ioe) { |
| String msg = |
| "error occurred deploying resource " + resource |
| + ", error " + ioe; |
| logger.warn(msg); |
| |
| } catch (SecurityException se) { |
| String msg = |
| "error occurred deploying resource " + resource |
| + ", error " + se; |
| logger.warn(msg); |
| } |
| } |
| |
| /** |
| * Deploy the specified classpath resources to the given target directory |
| * under the web application root directory. |
| * |
| * @param servletContext the web applications servlet context |
| * @param resources the array of classpath resource names |
| * @param targetDir the target directory to deploy the resource to |
| */ |
| public static void deployFiles(ServletContext servletContext, |
| String[] resources, String targetDir) { |
| |
| if (resources == null) { |
| throw new IllegalArgumentException("Null resources parameter"); |
| } |
| |
| for (String resource : resources) { |
| ClickUtils.deployFile(servletContext, resource, targetDir); |
| } |
| } |
| |
| /** |
| * Deploys required files (from a file list) for a control that repsects a specific convention. |
| * <p/> |
| * <b>Convention:</b> |
| * <p/> |
| * There's a descriptor file generated by the <code>tools/standalone/dev-tasks/ListFilesTask</code>. |
| * The files to deploy are all in a subdirectory placed in the same directory with the control. |
| * See documentation for more details. <p/> |
| * |
| * <b>Usage:</b><p/> |
| * In your Control simply use the code below, and everything should work automatically. |
| * <pre class="prettyprint"> |
| * public void onDeploy(ServletContext servletContext) { |
| * ClickUtils.deployFileList(servletContext, HeavyControl.class, "click"); |
| * } </pre> |
| * |
| * @param servletContext the web applications servlet context |
| * @param controlClass the class of the Control that has files for deployment |
| * @param targetDir target directory where to deploy the files to. In most cases this |
| * is only the reserved directory <code>click</code> |
| */ |
| public static void deployFileList(ServletContext servletContext, |
| Class<? extends Control> controlClass, String targetDir) { |
| |
| String packageName = ClassUtils.getPackageName(controlClass); |
| packageName = StringUtils.replaceChars(packageName, '.', '/'); |
| packageName = "/" + packageName; |
| String controlName = ClassUtils.getShortClassName(controlClass); |
| |
| ConfigService configService = getConfigService(servletContext); |
| LogService logService = configService.getLogService(); |
| String descriptorFile = packageName + "/" + controlName + ".files"; |
| logService.debug("Use deployment descriptor file:" + descriptorFile); |
| |
| try { |
| InputStream is = getResourceAsStream(descriptorFile, ClickUtils.class); |
| List fileList = IOUtils.readLines(is); |
| if (fileList == null || fileList.isEmpty()) { |
| logService.info("there are no files to deploy for control " + controlClass.getName()); |
| return; |
| } |
| |
| // a target dir list is required cause the ClickUtils.deployFile() is too inflexible to autodetect |
| // required subdirectories. |
| List<String> targetDirList = new ArrayList<String>(fileList.size()); |
| for (int i = 0; i < fileList.size(); i++) { |
| String filePath = (String) fileList.get(i); |
| String destination = ""; |
| int index = filePath.lastIndexOf('/'); |
| if (index != -1) { |
| destination = filePath.substring(0, index + 1); |
| } |
| targetDirList.add(i, targetDir + "/" + destination); |
| fileList.set(i, packageName + "/" + filePath); |
| } |
| |
| for (int i = 0; i < fileList.size(); i++) { |
| String source = (String) fileList.get(i); |
| String targetDirName = targetDirList.get(i); |
| ClickUtils.deployFile(servletContext, source, targetDirName); |
| } |
| |
| } catch (IOException e) { |
| String msg = "error occurred getting resource " + descriptorFile + ", error " + e; |
| logService.warn(msg); |
| } |
| } |
| |
| /** |
| * Return an encoded version of the <tt>Serializable</tt> object. The object |
| * will be serialized, compressed and Base 64 encoded. |
| * |
| * @param object the object to encode |
| * @return a serialized, compressed and Base 64 string encoding of the |
| * given object |
| * @throws IOException if an I/O error occurs |
| * @throws IllegalArgumentException if the object parameter is null, or if |
| * the object is not Serializable |
| */ |
| public static String encode(Object object) throws IOException { |
| if (object == null) { |
| throw new IllegalArgumentException("null object parameter"); |
| } |
| if (!(object instanceof Serializable)) { |
| throw new IllegalArgumentException("parameter not Serializable"); |
| } |
| |
| ByteArrayOutputStream bos = null; |
| GZIPOutputStream gos = null; |
| ObjectOutputStream oos = null; |
| |
| try { |
| bos = new ByteArrayOutputStream(); |
| gos = new GZIPOutputStream(bos); |
| oos = new ObjectOutputStream(gos); |
| |
| oos.writeObject(object); |
| |
| } finally { |
| close(oos); |
| close(gos); |
| close(bos); |
| } |
| |
| Base64 base64 = new Base64(); |
| |
| try { |
| byte[] byteData = base64.encode(bos.toByteArray()); |
| |
| return new String(byteData); |
| |
| } catch (Throwable t) { |
| String message = |
| "error occurred Base64 encoding: " + object + " : " + t; |
| throw new IOException(message); |
| } |
| } |
| |
| /** |
| * Return an object from the {@link #encode(Object)} string. |
| * |
| * @param string the encoded string |
| * @return an object from the encoded |
| * @throws ClassNotFoundException if the class could not be instantiated |
| * @throws IOException if an data I/O error occurs |
| */ |
| public static Object decode(String string) |
| throws ClassNotFoundException, IOException { |
| |
| Base64 base64 = new Base64(); |
| byte[] byteData = null; |
| |
| try { |
| byteData = base64.decode(string.getBytes()); |
| |
| } catch (Throwable t) { |
| String message = |
| "error occurred Base64 decoding: " + string + " : " + t; |
| throw new IOException(message); |
| } |
| |
| ByteArrayInputStream bis = null; |
| GZIPInputStream gis = null; |
| ObjectInputStream ois = null; |
| try { |
| bis = new ByteArrayInputStream(byteData); |
| gis = new GZIPInputStream(bis); |
| ois = new ObjectInputStream(gis); |
| |
| return ois.readObject(); |
| |
| } finally { |
| close(ois); |
| close(gis); |
| close(bis); |
| } |
| } |
| |
| /** |
| * Builds a cookie string containing a username and password. |
| * <p/> |
| * Note: with open source this is not really secure, but it prevents users |
| * from snooping the cookie file of others and by changing the XOR mask and |
| * character offsets, you can easily tweak results. |
| * <p/> |
| * This method was derived from Atlassian <tt>CookieUtils</tt> method of |
| * the same name, release under the BSD License. |
| * |
| * @param username the username |
| * @param password the password |
| * @param xorMask the XOR mask to encrypt the value with, must be same as |
| * as the value used to decrypt the cookie password |
| * @return String encoding the input parameters, an empty string if one of |
| * the arguments equals <code>null</code> |
| */ |
| public static String encodePasswordCookie(String username, String password, int xorMask) { |
| String encoding = new String(new char[]{DELIMITER, ENCODE_CHAR_OFFSET1, ENCODE_CHAR_OFFSET2}); |
| |
| return encodePasswordCookie(username, password, encoding, xorMask); |
| } |
| |
| /** |
| * Builds a cookie string containing a username and password, using offsets |
| * to customize the encoding. |
| * <p/> |
| * Note: with open source this is not really secure, but it prevents users |
| * from snooping the cookie file of others and by changing the XOR mask and |
| * character offsets, you can easily tweak results. |
| * <p/> |
| * This method was derived from Atlassian <tt>CookieUtils</tt> method of |
| * the same name, release under the BSD License. |
| * |
| * @param username the username |
| * @param password the password |
| * @param encoding a String used to customize cookie encoding (only the first 3 characters are used) |
| * @param xorMask the XOR mask to encrypt the value with, must be same as |
| * as the value used to decrypt the cookie password |
| * @return String encoding the input parameters, an empty string if one of |
| * the arguments equals <code>null</code>. |
| */ |
| public static String encodePasswordCookie(String username, String password, String encoding, int xorMask) { |
| StringBuilder buf = new StringBuilder(); |
| |
| if (username != null && password != null) { |
| |
| char offset1 = (encoding != null && encoding.length() > 1) |
| ? encoding.charAt(1) : ENCODE_CHAR_OFFSET1; |
| |
| char offset2 = (encoding != null && encoding.length() > 2) |
| ? encoding.charAt(2) : ENCODE_CHAR_OFFSET2; |
| |
| byte[] bytes = (username + DELIMITER + password).getBytes(); |
| int b; |
| |
| for (int n = 0; n < bytes.length; n++) { |
| b = bytes[n] ^ (xorMask + n); |
| buf.append((char) (offset1 + (b & 0x0F))); |
| buf.append((char) (offset2 + ((b >> 4) & 0x0F))); |
| } |
| } |
| |
| return buf.toString(); |
| } |
| |
| /** |
| * Decodes a cookie string containing a username and password. |
| * <p/> |
| * This method was derived from Atlassian <tt>CookieUtils</tt> method of |
| * the same name, release under the BSD License. |
| * |
| * @param cookieVal the encoded cookie username and password value |
| * @param xorMask the XOR mask to decrypt the value with, must be same as |
| * as the value used to encrypt the cookie password |
| * @return String[] containing the username at index 0 and the password at |
| * index 1, or <code>{ null, null }</code> if cookieVal equals |
| * <code>null</code> or the empty string. |
| */ |
| public static String[] decodePasswordCookie(String cookieVal, int xorMask) { |
| String encoding = new String(new char[]{DELIMITER, ENCODE_CHAR_OFFSET1, ENCODE_CHAR_OFFSET2}); |
| |
| return decodePasswordCookie(cookieVal, encoding, xorMask); |
| } |
| |
| /** |
| * Decodes a cookie string containing a username and password. |
| * <p/> |
| * This method was derived from Atlassian <tt>CookieUtils</tt> method of |
| * the same name, release under the BSD License. |
| * |
| * @param cookieVal the encoded cookie username and password value |
| * @param encoding a String used to customize cookie encoding (only the first 3 characters are used) |
| * - should be the same string you used to encode the cookie! |
| * @param xorMask the XOR mask to decrypt the value with, must be same as |
| * as the value used to encrypt the cookie password |
| * @return String[] containing the username at index 0 and the password at |
| * index 1, or <code>{ null, null }</code> if cookieVal equals |
| * <code>null</code> or the empty string. |
| */ |
| public static String[] decodePasswordCookie(String cookieVal, String encoding, |
| int xorMask) { |
| |
| // Check that the cookie value isn't null or zero-length |
| if (cookieVal == null || cookieVal.length() <= 0) { |
| return null; |
| } |
| |
| char offset1 = (encoding != null && encoding.length() > 1) |
| ? encoding.charAt(1) : ENCODE_CHAR_OFFSET1; |
| |
| char offset2 = (encoding != null && encoding.length() > 2) |
| ? encoding.charAt(2) : ENCODE_CHAR_OFFSET2; |
| |
| // Decode the cookie value |
| char[] chars = cookieVal.toCharArray(); |
| byte[] bytes = new byte[chars.length / 2]; |
| int b; |
| |
| for (int n = 0, m = 0; n < bytes.length; n++) { |
| b = chars[m++] - offset1; |
| b |= (chars[m++] - offset2) << 4; |
| bytes[n] = (byte) (b ^ (xorMask + n)); |
| } |
| |
| cookieVal = new String(bytes); |
| int pos = cookieVal.indexOf(DELIMITER); |
| |
| String username = (pos < 0) ? "" : cookieVal.substring(0, pos); |
| String password = (pos < 0) ? "" : cookieVal.substring(pos + 1); |
| |
| return new String[]{username, password}; |
| } |
| |
| /** |
| * URL encode the specified value using the "UTF-8" encoding scheme. |
| * <p/> |
| * For example <tt>(http://host?name=value with spaces)</tt> will become |
| * <tt>(http://host?name=value+with+spaces)</tt>. |
| * <p/> |
| * This method uses {@link URLEncoder#encode(java.lang.String, java.lang.String)} |
| * internally. |
| * |
| * @param value the value to encode using "UTF-8" |
| * @return an encoded URL string |
| */ |
| public static String encodeURL(Object value) { |
| if (value == null) { |
| throw new IllegalArgumentException("Null object parameter"); |
| } |
| |
| try { |
| return URLEncoder.encode(value.toString(), "UTF-8"); |
| } catch (UnsupportedEncodingException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| /** |
| * URL decode the specified value using the "UTF-8" encoding scheme. |
| * <p/> |
| * For example <tt>(http://host?name=value+with+spaces)</tt> will become |
| * <tt>(http://host?name=value with spaces)</tt>. |
| * <p/> |
| * This method uses {@link URLDecoder#decode(java.lang.String, java.lang.String)} |
| * internally. |
| * |
| * @param value the value to decode using "UTF-8" |
| * @return an encoded URL string |
| */ |
| public static String decodeURL(Object value) { |
| if (value == null) { |
| throw new IllegalArgumentException("Null object parameter"); |
| } |
| |
| try { |
| return URLDecoder.decode(value.toString(), "UTF-8"); |
| } catch (UnsupportedEncodingException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| /** |
| * Return an encoded URL value for the given object using the context |
| * request character encoding or "UTF-8" if the request character encoding |
| * is not specified. |
| * <p/> |
| * For example <tt>(http://host?name=value with spaces)</tt> will become |
| * <tt>(http://host?name=value+with+spaces)</tt>. |
| * <p/> |
| * This method uses |
| * {@link URLEncoder#encode(java.lang.String, java.lang.String)} internally. |
| * |
| * @param object the object value to encode as a URL string |
| * @param context the context providing the request character encoding |
| * @return an encoded URL string |
| */ |
| public static String encodeUrl(Object object, Context context) { |
| if (object == null) { |
| throw new IllegalArgumentException("Null object parameter"); |
| } |
| if (context == null) { |
| throw new IllegalArgumentException("Null context parameter"); |
| } |
| |
| String charset = context.getRequest().getCharacterEncoding(); |
| |
| try { |
| if (charset == null) { |
| return URLEncoder.encode(object.toString(), "UTF-8"); |
| |
| } else { |
| return URLEncoder.encode(object.toString(), charset); |
| } |
| |
| } catch (UnsupportedEncodingException uee) { |
| throw new RuntimeException(uee); |
| } |
| } |
| |
| /** |
| * Return a HTML escaped string for the given string value. |
| * |
| * @param value the string value to escape |
| * @return the HTML escaped string value |
| */ |
| public static String escapeHtml(String value) { |
| if (requiresHtmlEscape(value)) { |
| |
| HtmlStringBuffer buffer = new HtmlStringBuffer(value.length() * 2); |
| |
| buffer.appendHtmlEscaped(value); |
| |
| return buffer.toString(); |
| |
| } else { |
| return value; |
| } |
| } |
| |
| /** |
| * Return an escaped string for the given string value. The following |
| * characters are escaped: <, >, ", ', &. |
| * |
| * @param value the string value to escape |
| * @return the escaped string value |
| */ |
| public static String escape(String value) { |
| if (requiresEscape(value)) { |
| |
| HtmlStringBuffer buffer = new HtmlStringBuffer(value.length() * 2); |
| |
| buffer.appendEscaped(value); |
| |
| return buffer.toString(); |
| |
| } else { |
| return value; |
| } |
| } |
| |
| /** |
| * Return true if the control has a submitted request value, false otherwise. |
| * |
| * @param control the control which request parameter to check |
| * @return true if the control has a submitted request value, false otherwise |
| */ |
| public static boolean hasRequestParameter(Control control) { |
| Context context = Context.getThreadLocalContext(); |
| if (canBind(control, context)) { |
| return context.hasRequestParameter(control.getName()); |
| } |
| return false; |
| } |
| |
| /** |
| * Invoke the named method on the given object and return the boolean |
| * result. |
| * |
| * @see org.apache.click.Control#setListener(Object, String) |
| * |
| * @param listener the object with the method to invoke |
| * @param method the name of the method to invoke |
| * @return true if the listener method returned true |
| */ |
| public static boolean invokeListener(Object listener, String method) { |
| |
| Object result = invokeMethod(listener, method); |
| |
| if (result instanceof Boolean) { |
| return (Boolean) result; |
| |
| } else { |
| |
| Method targetMethod = null; |
| try { |
| targetMethod = listener.getClass().getMethod(method); |
| |
| String msg = |
| "Invalid listener method, missing boolean return type: " |
| + targetMethod; |
| throw new RuntimeException(msg); |
| } catch (Exception e) { |
| String msg = "Exception occurred invoking public method: " + targetMethod; |
| throw new RuntimeException(msg, e); |
| } |
| } |
| } |
| |
| /** |
| * Invoke the named method on the given target and return the Object result. |
| * |
| * @param target the target object with the method to invoke |
| * @param method the name of the method to invoke |
| * @return an ActionResult instance |
| */ |
| public static ActionResult invokeAction(Object target, String method) { |
| |
| Object result = invokeMethod(target, method); |
| |
| if (result == null || result instanceof ActionResult) { |
| return (ActionResult) result; |
| |
| } else { |
| |
| Method targetMethod = null; |
| try { |
| targetMethod = target.getClass().getMethod(method); |
| |
| String msg = |
| "Invalid target method, missing ActionResult return type: " |
| + targetMethod; |
| throw new RuntimeException(msg); |
| } catch (Exception e) { |
| String msg = "Exception occurred invoking public method: " + targetMethod; |
| throw new RuntimeException(msg, e); |
| } |
| } |
| } |
| |
| /** |
| * Return true if static web content resource versioning is enabled. |
| * |
| * @param context the request context |
| * @return true if static web content resource versioning is enabled |
| */ |
| public static boolean isEnableResourceVersion(Context context) { |
| return "true".equals(context.getRequestAttribute(ENABLE_RESOURCE_VERSION)); |
| } |
| |
| /** |
| * Return the value string limited to maxlength characters. If the string |
| * gets curtailed, "..." is appended to it. |
| * <p/> |
| * Adapted from Velocity Tools Formatter. |
| * |
| * @param value the string value to limit the length of |
| * @param maxlength the maximum string length |
| * @return a length limited string |
| */ |
| public static String limitLength(String value, int maxlength) { |
| return limitLength(value, maxlength, "..."); |
| } |
| |
| /** |
| * Return the value string limited to maxlength characters. If the string |
| * gets curtailed and the suffix parameter is appended to it. |
| * <p/> |
| * Adapted from Velocity Tools Formatter. |
| * |
| * @param value the string value to limit the length of |
| * @param maxlength the maximum string length |
| * @param suffix the suffix to append to the length limited string |
| * @return a length limited string |
| */ |
| public static String limitLength(String value, int maxlength, String suffix) { |
| String ret = value; |
| if (value.length() > maxlength) { |
| ret = value.substring(0, maxlength - suffix.length()) + suffix; |
| } |
| return ret; |
| } |
| |
| /** |
| * Return the application LogService instance using thread local Context |
| * to perform the lookup. |
| * |
| * @return the application LogService instance |
| */ |
| public static LogService getLogService() { |
| Context context = Context.getThreadLocalContext(); |
| ServletContext servletContext = context.getServletContext(); |
| ConfigService configService = getConfigService(servletContext); |
| LogService logService = configService.getLogService(); |
| return logService; |
| } |
| |
| /** |
| * Return the list of Fields for the given Form, including any Fields |
| * contained in FieldSets. The list of returned fields will exclude any |
| * <tt>Button</tt>, <tt>FieldSet</tt> or <tt>Label</tt> fields. |
| * |
| * @param form the form to obtain the fields from |
| * @return the list of contained form fields |
| */ |
| public static List<Field> getFormFields(Form form) { |
| if (form == null) { |
| throw new IllegalArgumentException("Null form parameter"); |
| } |
| return ContainerUtils.getInputFields(form); |
| } |
| |
| /** |
| * Return the mime-type or content-type for the given filename/extension. |
| * <p/> |
| * Example: |
| * <pre class="prettyprint"> |
| * // Lookup mimetype for file |
| * String mimeType = ClickUtils.getMimeType("hello-world.pdf"); |
| * |
| * // Lookup mimetype for extension |
| * mimeType = ClickUtils.getMimeType("pdf"); |
| * </pre> |
| * |
| * @param value the filename or extension to obtain the mime-type for |
| * @return the mime-type for the given filename/extension, or null if not |
| * found |
| */ |
| public static String getMimeType(String value) { |
| if (value == null) { |
| throw new IllegalArgumentException("null filename/extension parameter"); |
| } |
| |
| String ext = value; |
| |
| int index = value.lastIndexOf("."); |
| if (index != -1) { |
| ext = value.substring(index + 1); |
| } |
| |
| try { |
| ResourceBundle bundle = getBundle("org/apache/click/util/mime-type"); |
| |
| return bundle.getString(ext.toLowerCase()); |
| |
| } catch (MissingResourceException mre) { |
| return null; |
| } |
| } |
| |
| /** |
| * Return the given control's top level parent's localized messages Map. |
| * <p/> |
| * This method will walk up to the control's parent page object and |
| * return pages messages. If the control's top level parent is a control |
| * then the parent's messages map will be returned. If the top level |
| * parent is not a Page or Control instance an empty map will be returned. |
| * |
| * @param control the control to get the parent messages Map for |
| * @return the top level parent's Map of localized messages |
| */ |
| public static Map<String, String> getParentMessages(Control control) { |
| if (control == null) { |
| throw new IllegalArgumentException("Null control parameter"); |
| } |
| |
| Object parent = control.getParent(); |
| if (parent == null) { |
| return Collections.emptyMap(); |
| |
| } else { |
| while (parent != null) { |
| if (parent instanceof Control) { |
| control = (Control) parent; |
| parent = control.getParent(); |
| |
| if (parent == null) { |
| return control.getMessages(); |
| } |
| |
| } else if (parent instanceof Page) { |
| Page page = (Page) parent; |
| return page.getMessages(); |
| |
| } else if (parent != null) { |
| // Unknown parent class |
| return Collections.emptyMap(); |
| } |
| } |
| } |
| |
| return Collections.emptyMap(); |
| } |
| |
| /** |
| * Return the given control's top level parent's localized message for the |
| * specified name. |
| * <p/> |
| * This method will walk up to the control's parent page object and for each |
| * parent control found, look for a message of the specified name. A |
| * message found in a parent control will override the message of a child |
| * control. |
| * <p/> |
| * Given the following property files: |
| * <p/> |
| * MyPage.properties |
| * <pre class="prettyprint"> |
| * myfield.label=Page </pre> |
| * |
| * and MyForm.properties |
| * <pre class="prettyprint"> |
| * myfield.label=Form </pre> |
| * |
| * and a the following snippet: |
| * |
| * <pre class="prettyprint"> |
| * public MyPage extends Page { |
| * public void onInit() { |
| * MyForm form = new MyForm("form"); |
| * TextField field = new TextField("myfield"); |
| * form.add(field); |
| * |
| * //1. |
| * System.out.println(ClickUtils.getParentMessage(field, "myfield.label")); |
| * |
| * addControl(form); |
| * |
| * //2. |
| * System.out.println(ClickUtils.getParentMessage(field, "myfield.label")); |
| * } |
| * } |
| * </pre> |
| * |
| * The first (1.) println statement above will output <tt>Form</tt> because |
| * at that stage MyForm is the highest level parent of field. |
| * <tt>getParentMessage</tt> will find the property <tt>myfield.label</tt> |
| * in the MyForm message properties and return <tt>Form</tt> |
| * <p/> |
| * The second (2.) println statement will output <tt>Page</tt> as now |
| * MyPage is the highest level parent. On its first pass up the hierarchy, |
| * <tt>getParentMessage</tt> will find the property <tt>myfield.label</tt> |
| * in the MyForm message properties and on its second pass will find the |
| * same property in MyPage message properties. As MyPage is higher up the |
| * hierarchy than MyForm, MyPage will override MyForm and the property value |
| * will be <tt>Page</tt>. |
| * |
| * @param control the control to get the parent message for |
| * @param name the specific property name to find |
| * @return the top level parent's Map of localized messages |
| */ |
| public static String getParentMessage(Control control, String name) { |
| if (control == null) { |
| throw new IllegalArgumentException("Null control parameter"); |
| } |
| if (name == null) { |
| throw new IllegalArgumentException("Null name parameter"); |
| } |
| |
| Object parent = control.getParent(); |
| if (parent == null) { |
| return null; |
| |
| } else { |
| String message = null; |
| while (parent != null) { |
| if (parent instanceof Control) { |
| control = (Control) parent; |
| if (control != null) { |
| if (control.getMessages().containsKey(name)) { |
| message = control.getMessages().get(name); |
| } |
| } |
| |
| parent = control.getParent(); |
| if (parent == null) { |
| return message; |
| } |
| |
| } else if (parent instanceof Page) { |
| Page page = (Page) parent; |
| if (page.getMessages().containsKey(name)) { |
| message = page.getMessages().get(name); |
| } |
| return message; |
| |
| } else if (parent != null) { |
| // Unknown parent class |
| return null; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Get the parent page of the given control or null if the control has no |
| * parent. This method will walk up the control's parent hierarchy to |
| * find its parent page. |
| * |
| * @param control the control to get the parent page from |
| * @return the parent page of the control or null if the control has no |
| * parent |
| */ |
| public static Page getParentPage(Control control) { |
| Object parent = control.getParent(); |
| |
| while (parent != null) { |
| if (parent instanceof Control) { |
| control = (Control) parent; |
| parent = control.getParent(); |
| |
| } else if (parent instanceof Page) { |
| return (Page) parent; |
| |
| } else if (parent != null) { |
| throw new RuntimeException("Invalid parent class"); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Return an ordered map of request parameters from the given request. |
| * |
| * @param request the servlet request to obtain request parameters from |
| * @return the ordered map of request parameters |
| */ |
| public static Map<String, Object> getRequestParameterMap(HttpServletRequest request) { |
| |
| TreeMap<String, Object> requestParams = new TreeMap<String, Object>(); |
| |
| Enumeration<?> paramNames = request.getParameterNames(); |
| while (paramNames.hasMoreElements()) { |
| String name = paramNames.nextElement().toString(); |
| |
| String[] values = request.getParameterValues(name); |
| |
| if (values.length == 1) { |
| requestParams.put(name, values[0]); |
| |
| } else { |
| requestParams.put(name, values); |
| } |
| } |
| |
| return requestParams; |
| } |
| |
| /** |
| * Return the page resource path from the request. For example: |
| * <pre class="codeHtml"> |
| * <span class="blue">http://www.mycorp.com/banking/secure/login.htm</span> -> <span class="red">/secure/login.htm</span> </pre> |
| * |
| * @param request the page servlet request |
| * @return the page resource path from the request |
| */ |
| public static String getResourcePath(HttpServletRequest request) { |
| // Adapted from VelocityViewServlet.handleRequest() method: |
| |
| // If we get here from RequestDispatcher.include(), getServletPath() |
| // will return the original (wrong) URI requested. The following |
| // special attribute holds the correct path. See section 8.3 of the |
| // Servlet 2.3 specification. |
| |
| String path = (String) |
| request.getAttribute("javax.servlet.include.servlet_path"); |
| |
| // Also take into account the PathInfo stated on |
| // SRV.4.4 Request Path Elements. |
| String info = (String) |
| request.getAttribute("javax.servlet.include.path_info"); |
| |
| if (path == null) { |
| path = request.getServletPath(); |
| info = request.getPathInfo(); |
| } |
| |
| if (info != null) { |
| path += info; |
| } |
| |
| return path; |
| } |
| |
| /** |
| * Return the requestURI from the request. For example: |
| * <pre class="codeHtml"> |
| * <span class="blue">http://www.mycorp.com/banking/secure/login.htm</span> -> <span class="red">/banking/secure/login.htm</span> </pre> |
| * |
| * @param request the page servlet request |
| * @return the requestURI from the request |
| */ |
| public static String getRequestURI(HttpServletRequest request) { |
| // CLK-334. Adapted from VelocityViewServlet.handleRequest() method: |
| |
| // If we get here from RequestDispatcher.include(), getServletPath() |
| // will return the original (wrong) URI requested. The following |
| // special attribute holds the correct path. See section 8.3 of the |
| // Servlet 2.3 specification. |
| |
| String requestURI = (String) request.getAttribute("javax.servlet.include.request_uri"); |
| |
| if (requestURI == null) { |
| requestURI = request.getRequestURI(); |
| } |
| |
| if (requestURI != null && requestURI.endsWith(".jsp")) { |
| requestURI = StringUtils.replace(requestURI, ".jsp", ".htm"); |
| } |
| |
| return requestURI; |
| } |
| |
| /** |
| * Finds a resource with a given name. This method returns null if no |
| * resource with this name is found. |
| * <p> |
| * This method uses the current <tt>Thread</tt> context <tt>ClassLoader</tt> to find |
| * the resource. If the resource is not found the class loader of the given |
| * class is then used to find the resource. |
| * |
| * @param name the name of the resource |
| * @param aClass the class lookup the resource against, if the resource is |
| * not found using the current <tt>Thread</tt> context <tt>ClassLoader</tt>. |
| * @return the input stream of the resource if found or null otherwise |
| */ |
| public static InputStream getResourceAsStream(String name, Class<?> aClass) { |
| Validate.notNull(name, "Parameter name is null"); |
| Validate.notNull(aClass, "Parameter aClass is null"); |
| |
| ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); |
| |
| InputStream inputStream = classLoader.getResourceAsStream(name); |
| if (inputStream == null) { |
| inputStream = aClass.getResourceAsStream(name); |
| } |
| |
| return inputStream; |
| } |
| |
| /** |
| * Finds a resource with a given name. This method returns null if no |
| * resource with this name is found. |
| * <p> |
| * This method uses the current <tt>Thread</tt> context <tt>ClassLoader</tt> to find |
| * the resource. If the resource is not found the class loader of the given |
| * class is then used to find the resource. |
| * |
| * @param name the name of the resource |
| * @param aClass the class lookup the resource against, if the resource is |
| * not found using the current <tt>Thread</tt> context <tt>ClassLoader</tt>. |
| * @return the URL of the resource if found or null otherwise |
| */ |
| public static URL getResource(String name, Class<?> aClass) { |
| Validate.notNull(name, "Parameter name is null"); |
| Validate.notNull(aClass, "Parameter aClass is null"); |
| |
| ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); |
| |
| URL url = classLoader.getResource(name); |
| if (url == null) { |
| url = aClass.getResource(name); |
| } |
| |
| return url; |
| } |
| |
| /** |
| * Remove the control state from the session for the given stateful control, |
| * control name and request context. |
| * |
| * @param control the stateful control which state to remove |
| * @param controlName the name of the control which state to remove |
| * @param context the request context |
| */ |
| public static void removeState(Stateful control, String controlName, Context context) { |
| if (control == null) { |
| throw new IllegalStateException("Control cannot be null."); |
| } |
| if (controlName == null) { |
| throw new IllegalStateException(ClassUtils.getShortClassName(control.getClass()) |
| + " name has not been set. State cannot be removed until the name is set"); |
| } |
| if (context == null) { |
| throw new IllegalStateException("Context cannot be null."); |
| } |
| |
| String resourcePath = context.getResourcePath(); |
| Map pageMap = ClickUtils.getPageState(resourcePath, context); |
| if (pageMap != null) { |
| Object pop = pageMap.remove(controlName); |
| |
| if (pageMap.isEmpty()) { |
| // If this was the last state for the page, remove the page state map |
| context.removeSessionAttribute(resourcePath); |
| } else { |
| // Check if control state was emoved |
| if (pop != null) { |
| // If control state was removed, set session attribute to force |
| // session replication in a cluster |
| context.setSessionAttribute(resourcePath, pageMap); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Restore the control state from the session for the given stateful control, |
| * control name and request context. |
| * <p/> |
| * This method delegates to {@link org.apache.click.Stateful#setState(java.lang.Object)} |
| * to restore the control state. |
| * |
| * @param control the stateful control which state to restore |
| * @param controlName the name of the control which state to restore |
| * @param context the request context |
| */ |
| public static void restoreState(Stateful control, String controlName, Context context) { |
| if (control == null) { |
| throw new IllegalStateException("Control cannot be null."); |
| } |
| if (controlName == null) { |
| throw new IllegalStateException(ClassUtils.getShortClassName(control.getClass()) |
| + " name has not been set. State cannot be restored until the name is set"); |
| } |
| if (context == null) { |
| throw new IllegalStateException("Context cannot be null."); |
| } |
| |
| String resourcePath = context.getResourcePath(); |
| Map pageMap = ClickUtils.getPageState(resourcePath, context); |
| if (pageMap != null) { |
| control.setState(pageMap.get(controlName)); |
| } |
| } |
| |
| /** |
| * Save the control state in the session for the given stateful control, |
| * control name and request context. |
| * <p/> |
| * * This method delegates to {@link org.apache.click.Stateful#getState()} |
| * to retrieve the control state to save. |
| * |
| * @param control the stateful control which state to save |
| * @param controlName the name of the control control which state to save |
| * @param context the request context |
| */ |
| public static void saveState(Stateful control, String controlName, Context context) { |
| if (control == null) { |
| throw new IllegalStateException("Control cannot be null."); |
| } |
| if (controlName == null) { |
| throw new IllegalStateException(ClassUtils.getShortClassName(control.getClass()) |
| + " name has not been set. State cannot be saved until the name is set"); |
| } |
| if (context == null) { |
| throw new IllegalStateException("Context cannot be null."); |
| } |
| |
| String resourcePath = context.getResourcePath(); |
| Map pageMap = getOrCreatePageState(resourcePath, context); |
| Object state = control.getState(); |
| if (state == null) { |
| // Set null state to see if it differs from previous state |
| Object pop = pageMap.put(controlName, state); |
| if (pop != null) { |
| // Previous state differs from current state, so set the |
| // session attribute to force session replication in a cluster |
| context.setSessionAttribute(resourcePath, pageMap); |
| } |
| } else { |
| pageMap.put(controlName, state); |
| // After control state has been added to the page state, set the |
| // session attribute to force session replication in a cluster |
| context.setSessionAttribute(resourcePath, pageMap); |
| } |
| } |
| |
| /** |
| * Return the getter method name for the given property name. |
| * |
| * @param property the property name |
| * @return the getter method name for the given property name. |
| */ |
| public static String toGetterName(String property) { |
| HtmlStringBuffer buffer = new HtmlStringBuffer(property.length() + 3); |
| |
| buffer.append("get"); |
| buffer.append(Character.toUpperCase(property.charAt(0))); |
| buffer.append(property.substring(1)); |
| |
| return buffer.toString(); |
| } |
| |
| /** |
| * Return the is getter method name for the given property name. |
| * |
| * @param property the property name |
| * @return the is getter method name for the given property name. |
| */ |
| public static String toIsGetterName(String property) { |
| HtmlStringBuffer buffer = new HtmlStringBuffer(property.length() + 3); |
| |
| buffer.append("is"); |
| buffer.append(Character.toUpperCase(property.charAt(0))); |
| buffer.append(property.substring(1)); |
| |
| return buffer.toString(); |
| } |
| |
| /** |
| * Return a field label string from the given field name. For example: |
| * <pre class="codeHtml"> |
| * <span class="blue">faxNumber</span> -> <span class="red">Fax Number</span> </pre> |
| * <p/> |
| * <b>Note</b> toLabel will return an empty String ("") if a <tt>null</tt> |
| * String name is specified. |
| * |
| * @param name the field name |
| * @return a field label string from the given field name |
| */ |
| public static String toLabel(String name) { |
| if (name == null) { |
| return ""; |
| } |
| |
| HtmlStringBuffer buffer = new HtmlStringBuffer(); |
| |
| for (int i = 0, size = name.length(); i < size; i++) { |
| char aChar = name.charAt(i); |
| |
| if (i == 0) { |
| buffer.append(Character.toUpperCase(aChar)); |
| |
| } else { |
| buffer.append(aChar); |
| |
| if (i < name.length() - 1) { |
| char nextChar = name.charAt(i + 1); |
| if (Character.isLowerCase(aChar) |
| && (Character.isUpperCase(nextChar) |
| || Character.isDigit(nextChar))) { |
| |
| // Add space before digits or uppercase letters |
| buffer.append(" "); |
| |
| } else if (Character.isDigit(aChar) |
| && (!Character.isDigit(nextChar))) { |
| |
| // Add space after digits |
| buffer.append(" "); |
| } |
| } |
| } |
| } |
| |
| return buffer.toString(); |
| } |
| |
| /** |
| * Return an 32 char MD5 encoded string from the given plain text. |
| * The returned value is MD5 hash compatible with Tomcat catalina Realm. |
| * <p/> |
| * Adapted from <tt>org.apache.catalina.util.MD5Encoder</tt> |
| * |
| * @param plaintext the plain text value to encode |
| * @return encoded MD5 string |
| */ |
| public static String toMD5Hash(String plaintext) { |
| if (plaintext == null) { |
| throw new IllegalArgumentException("Null plaintext parameter"); |
| } |
| try { |
| MessageDigest md = MessageDigest.getInstance("MD5"); |
| |
| md.update(plaintext.getBytes("UTF-8")); |
| |
| byte[] binaryData = md.digest(); |
| |
| char[] buffer = new char[32]; |
| |
| for (int i = 0; i < 16; i++) { |
| int low = (binaryData[i] & 0x0f); |
| int high = ((binaryData[i] & 0xf0) >> 4); |
| buffer[i * 2] = HEXADECIMAL[high]; |
| buffer[i * 2 + 1] = HEXADECIMAL[low]; |
| } |
| |
| return new String(buffer); |
| |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| /** |
| * Return a field name string from the given field label. |
| * <p/> |
| * A label of <tt>" OK do it!"</tt> is returned as <tt>"okDoIt"</tt>. Any <tt>&nbsp;</tt> |
| * characters will also be removed. |
| * <p/> |
| * A label of <tt>"customerSelect"</tt> is returned as <tt>"customerSelect"</tt>. |
| * |
| * @param label the field label or caption |
| * @return a field name string from the given field label |
| */ |
| public static String toName(String label) { |
| if (label == null) { |
| throw new IllegalArgumentException("Null label parameter"); |
| } |
| |
| boolean doneFirstLetter = false; |
| boolean lastCharBlank = false; |
| boolean hasWhiteSpace = (label.indexOf(' ') != -1); |
| |
| HtmlStringBuffer buffer = new HtmlStringBuffer(label.length()); |
| for (int i = 0, size = label.length(); i < size; i++) { |
| char aChar = label.charAt(i); |
| |
| if (aChar != ' ') { |
| if (Character.isJavaIdentifierPart(aChar)) { |
| if (lastCharBlank) { |
| if (doneFirstLetter) { |
| buffer.append(Character.toUpperCase(aChar)); |
| lastCharBlank = false; |
| } else { |
| buffer.append(Character.toLowerCase(aChar)); |
| lastCharBlank = false; |
| doneFirstLetter = true; |
| } |
| } else { |
| if (doneFirstLetter) { |
| if (hasWhiteSpace) { |
| buffer.append(Character.toLowerCase(aChar)); |
| } else { |
| buffer.append(aChar); |
| } |
| } else { |
| buffer.append(Character.toLowerCase(aChar)); |
| doneFirstLetter = true; |
| } |
| } |
| } |
| } else { |
| lastCharBlank = true; |
| } |
| } |
| |
| return buffer.toString(); |
| } |
| |
| /** |
| * Return the setter method name for the given property name. |
| * |
| * @param property the property name |
| * @return the setter method name for the given property name. |
| */ |
| public static String toSetterName(String property) { |
| HtmlStringBuffer buffer = new HtmlStringBuffer(property.length() + 3); |
| |
| buffer.append("set"); |
| buffer.append(Character.toUpperCase(property.charAt(0))); |
| buffer.append(property.substring(1)); |
| |
| return buffer.toString(); |
| } |
| |
| /** |
| * Returns true if Click resources (JavaScript, CSS, images etc) packaged |
| * in jars can be deployed to the root directory of the webapp, false |
| * otherwise. |
| * <p/> |
| * This method will return false in restricted environments where write |
| * access to the underlying file system is disallowed. Examples where |
| * write access is not allowed include the WebLogic JEE server (this can be |
| * changed though) and Google App Engine. |
| * |
| * @param servletContext the application servlet context |
| * @return true if writes are allowed, false otherwise |
| */ |
| public static boolean isResourcesDeployable(ServletContext servletContext) { |
| try { |
| boolean canWrite = (servletContext.getRealPath("/") != null); |
| if (!canWrite) { |
| return false; |
| } |
| |
| // Since Google App Engine returns a value for getRealPath, check |
| // SecurityManager if writes are allowed |
| SecurityManager security = System.getSecurityManager(); |
| if (security != null) { |
| security.checkWrite("/click"); |
| } |
| return true; |
| } catch (Throwable e) { |
| return false; |
| } |
| } |
| |
| // -------------------------------------------------------- Package Methods |
| |
| /** |
| * Append the escaped string for the given character value to the |
| * buffer. The following characters are escaped: <, >, ", ', |
| * &. |
| * |
| * @param aChar the character value to escape |
| * @param buffer the string buffer to append the escaped value to |
| */ |
| static void appendEscapeChar(char aChar, HtmlStringBuffer buffer) { |
| int index = aChar; |
| |
| if (index < XML_ENTITIES.length && XML_ENTITIES[index] != null) { |
| buffer.append(XML_ENTITIES[index]); |
| |
| } else { |
| buffer.append(aChar); |
| } |
| } |
| |
| /** |
| * Append the escaped string for the given character value to the buffer. |
| * The following characters are escaped: <, >, ", ', &. |
| * |
| * @param value the String value to escape |
| * @param buffer the string buffer to append the escaped value to |
| */ |
| static void appendEscapeString(String value, HtmlStringBuffer buffer) { |
| char aChar; |
| for (int i = 0, size = value.length(); i < size; i++) { |
| aChar = value.charAt(i); |
| int index = aChar; |
| |
| if (index < XML_ENTITIES.length && XML_ENTITIES[index] != null) { |
| buffer.append(XML_ENTITIES[index]); |
| |
| } else { |
| buffer.append(aChar); |
| } |
| } |
| } |
| |
| /** |
| * Append the HTML escaped string for the given character value to the |
| * buffer. |
| * |
| * @param aChar the character value to escape |
| * @param buffer the string buffer to append the escaped value to |
| */ |
| static void appendHtmlEscapeChar(char aChar, HtmlStringBuffer buffer) { |
| int index = aChar; |
| |
| if (index < HTML_ENTITIES.length && HTML_ENTITIES[index] != null) { |
| buffer.append(HTML_ENTITIES[index]); |
| |
| } else { |
| buffer.append(aChar); |
| } |
| } |
| |
| /** |
| * Append the HTML escaped string for the given character value to the |
| * buffer. |
| * |
| * @param value the String value to escape |
| * @param buffer the string buffer to append the escaped value to |
| */ |
| static void appendHtmlEscapeString(String value, HtmlStringBuffer buffer) { |
| char aChar; |
| for (int i = 0, size = value.length(); i < size; i++) { |
| aChar = value.charAt(i); |
| int index = aChar; |
| |
| if (index < HTML_ENTITIES.length && HTML_ENTITIES[index] != null) { |
| buffer.append(HTML_ENTITIES[index]); |
| |
| } else { |
| buffer.append(aChar); |
| } |
| } |
| } |
| |
| /** |
| * Return true if the given character requires escaping. The following |
| * characters are escaped: <, >, ", ', &. |
| * |
| * @param aChar the character value to test |
| * @return true if the given character requires escaping |
| */ |
| static boolean requiresEscape(char aChar) { |
| int index = aChar; |
| |
| if (index < XML_ENTITIES.length) { |
| return XML_ENTITIES[index] != null; |
| |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Return true if the given string requires escaping of characters. |
| * The following characters require escaping: <, >, ", ', &. |
| * |
| * @param value the string value to test |
| * @return true if the given string requires escaping of characters |
| */ |
| static boolean requiresEscape(String value) { |
| if (value == null) { |
| return false; |
| } |
| |
| int length = value.length(); |
| for (int i = 0; i < length; i++) { |
| if (requiresEscape(value.charAt(i))) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Return true if the given character requires HTML escaping. |
| * |
| * @param aChar the character value to test |
| * @return true if the given character requires HTML escaping |
| */ |
| static boolean requiresHtmlEscape(char aChar) { |
| int index = aChar; |
| |
| if (index < HTML_ENTITIES.length) { |
| return HTML_ENTITIES[index] != null; |
| |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Return true if the given string requires HTML escaping of characters. |
| * |
| * @param value the string value to test |
| * @return true if the given string requires HTML escaping of characters |
| */ |
| static boolean requiresHtmlEscape(String value) { |
| if (value == null) { |
| return false; |
| } |
| |
| int length = value.length(); |
| for (int i = 0; i < length; i++) { |
| if (requiresHtmlEscape(value.charAt(i))) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| // -------------------------------------------------------- Private Methods |
| |
| /** |
| * A helper method that binds the submitted request values of all Fields |
| * and Links inside the given container or child containers. |
| * <p/> |
| * For Field controls, this method delegates to |
| * {@link #bindField(org.apache.click.control.Field, org.apache.click.Context)}. |
| * |
| * @param container the container which Fields and Links to bind |
| * @param context the request context |
| */ |
| private static void bind(Container container, Context context) { |
| for (int i = 0; i < container.getControls().size(); i++) { |
| Control control = container.getControls().get(i); |
| if (control instanceof Container) { |
| // Include fields but skip fieldSets |
| if (control instanceof Field) { |
| Field field = (Field) control; |
| bindField(field, context); |
| |
| } else if (control instanceof AbstractLink) { |
| ((AbstractLink) control).bindRequestValue(); |
| } |
| Container childContainer = (Container) control; |
| bind(childContainer, context); |
| |
| } else if (control instanceof Field) { |
| Field field = (Field) control; |
| bindField(field, context); |
| |
| } else if (control instanceof AbstractLink) { |
| ((AbstractLink) control).bindRequestValue(); |
| } |
| } |
| } |
| |
| /** |
| * A helper method that binds and validates the submitted request values |
| * of all Fields and Links inside the given container or child containers. |
| * <p/> |
| * For Field controls, this method delegates to |
| * {@link #bindField(org.apache.click.control.Field, org.apache.click.Context)}. |
| * |
| * @param container the container which Fields and Links to bind and |
| * validate |
| * @param context the request context |
| * @return true if container fields and links was bound and valid, false |
| * otherwise |
| */ |
| private static boolean bindAndValidate(Container container, Context context) { |
| boolean valid = true; |
| for (int i = 0; i < container.getControls().size(); i++) { |
| Control control = container.getControls().get(i); |
| if (control instanceof Container) { |
| |
| // Bind and validate fields only |
| if (control instanceof Field) { |
| if (!bindAndValidate((Field) control, context)) { |
| valid = false; |
| } |
| |
| } else if (control instanceof AbstractLink) { |
| ((AbstractLink) control).bindRequestValue(); |
| } |
| |
| Container childContainer = (Container) control; |
| if (!bindAndValidate(childContainer, context)) { |
| valid = false; |
| } |
| |
| } else if (control instanceof Field) { |
| if (!bindAndValidate((Field) control, context)) { |
| valid = false; |
| } |
| |
| } else if (control instanceof AbstractLink) { |
| ((AbstractLink) control).bindRequestValue(); |
| } |
| } |
| return valid; |
| } |
| |
| /** |
| * A helper method that binds and validates the submitted request values |
| * of all Fields and Links inside the given Form or child containers. Note, |
| * the Form itself is also validated. |
| * <p/> |
| * For Field controls, this method delegates to |
| * {@link #bindField(org.apache.click.control.Field, org.apache.click.Context)}. |
| * |
| * @param form the Form which Fields and Links to bind and validate |
| * @param context the request context |
| * @return true if the form, it's fields and links was bound and valid, false |
| * otherwise |
| */ |
| private static boolean bindAndValidate(Form form, Context context) { |
| if (!bindAndValidate((Container) form, context)) { |
| return false; |
| } |
| |
| if (form.getValidate()) { |
| // Keep reference to current error |
| String errorReference = form.getError(); |
| |
| // Validate form. If validation fails the form error will be changed |
| form.validate(); |
| |
| boolean valid = form.isValid(); |
| |
| // Revert back to original error |
| form.setError(errorReference); |
| |
| return valid; |
| } |
| return true; |
| } |
| |
| /** |
| * A helper method that binds and validates the Field's submitted request |
| * value. |
| * <p/> |
| * This method delegates to |
| * {@link #bindField(org.apache.click.control.Field, org.apache.click.Context)} |
| * to bind the field value. |
| * |
| * @param field the Field to bind and validate |
| * @param context the request context |
| * @return true if field was bound and valid, or false otherwise |
| */ |
| private static boolean bindAndValidate(Field field, Context context) { |
| boolean continueProcessing = bindField(field, context); |
| if (!continueProcessing) { |
| return true; |
| } |
| |
| if (field.getValidate()) { |
| // Keep reference to current error |
| String errorReference = field.getError(); |
| |
| // Validate field. If validation fails the field error will be changed |
| field.validate(); |
| |
| boolean valid = field.isValid(); |
| |
| // Revert back to original error |
| field.setError(errorReference); |
| |
| return valid; |
| } |
| return true; |
| } |
| |
| /** |
| * Bind the field to its incoming request parameter, returning true if the |
| * field value was bound, false otherwise. |
| * <p/> |
| * <b>Please note</b>: this method won't bind disabled fields, |
| * unless the field has an incoming request parameter matching its name. |
| * If an incoming request parameter is present, this method will switch off |
| * the Field's disabled property. |
| * |
| * @param field the field which value to bind to its request parameter |
| * @param context the request context |
| * @return true if the field was bound to its request parameter, false |
| * otherwise |
| */ |
| private static boolean bindField(Field field, Context context) { |
| if (field.isDisabled()) { |
| // Switch off disabled property if control has incoming request |
| // parameter. Normally this means the field was enabled via JS |
| if (context.hasRequestParameter(field.getName())) { |
| field.setDisabled(false); |
| } else { |
| return false; |
| } |
| } |
| field.bindRequestValue(); |
| return true; |
| } |
| |
| /** |
| * Retrieve or create the map where page state is stored in. |
| * |
| * @see #getPageState(java.lang.String, org.apache.click.Context) |
| * |
| * @param pagePath the path under which the page state is stored in the |
| * session |
| * @param context the request context |
| * @return the map where page state is stored in |
| */ |
| private static Map getOrCreatePageState(String pagePath, Context context) { |
| Map pageMap = getPageState(pagePath, context); |
| if (pageMap == null) { |
| pageMap = new HashMap(); |
| } |
| return pageMap; |
| } |
| |
| /** |
| * Retrieve the map for the given pagePath from the session where page state |
| * is stored in. |
| * |
| * @param pagePath the path under which the page state is stored in the |
| * session |
| * @param context the request context |
| * @return the map where page state is stored in |
| */ |
| private static Map getPageState(String pagePath, Context context) { |
| Object storedPageValue = context.getSessionAttribute(pagePath); |
| Map pageMap = null; |
| if (storedPageValue != null) { |
| pageMap = (Map) storedPageValue; |
| } |
| return pageMap; |
| } |
| |
| /** |
| * Invoke the named method on the given target object and return the result. |
| * |
| * @param target the target object with the method to invoke |
| * @param method the name of the method to invoke |
| * @return Object the target method result |
| */ |
| private static Object invokeMethod(Object target, String method) { |
| if (target == null) { |
| throw new IllegalArgumentException("Null target parameter"); |
| } |
| if (method == null) { |
| throw new IllegalArgumentException("Null method parameter"); |
| } |
| |
| Method targetMethod = null; |
| boolean isAccessible = true; |
| try { |
| Class<?> targetClass = target.getClass(); |
| targetMethod = targetClass.getMethod(method); |
| |
| // Change accessible for anonymous inner classes public methods |
| // only. Conditional checks: |
| // #1 - Target method is not accessible |
| // #2 - Anonymous inner classes are not public |
| // #3 - Only modify public methods |
| // #4 - Anonymous inner classes have no declaring class |
| // #5 - Anonymous inner classes have $ in name |
| if (!targetMethod.isAccessible() |
| && !Modifier.isPublic(targetClass.getModifiers()) |
| && Modifier.isPublic(targetMethod.getModifiers()) |
| && targetClass.getDeclaringClass() == null |
| && targetClass.getName().indexOf('$') != -1) { |
| |
| isAccessible = false; |
| targetMethod.setAccessible(true); |
| } |
| |
| return targetMethod.invoke(target); |
| |
| } catch (InvocationTargetException ite) { |
| |
| Throwable e = ite.getTargetException(); |
| if (e instanceof RuntimeException) { |
| throw (RuntimeException) e; |
| |
| } else if (e instanceof Exception) { |
| String msg = |
| "Exception occurred invoking public method: " + targetMethod; |
| |
| throw new RuntimeException(msg, e); |
| |
| } else if (e instanceof Error) { |
| String msg = |
| "Error occurred invoking public method: " + targetMethod; |
| |
| throw new RuntimeException(msg, e); |
| |
| } else { |
| String msg = |
| "Error occurred invoking public method: " + targetMethod; |
| |
| throw new RuntimeException(msg, e); |
| } |
| |
| } catch (Exception e) { |
| String msg = |
| "Exception occurred invoking public method: " + targetMethod; |
| |
| throw new RuntimeException(msg, e); |
| |
| } finally { |
| if (targetMethod != null && !isAccessible) { |
| targetMethod.setAccessible(false); |
| } |
| } |
| } |
| } |