Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.ajax4jsf.javascript.ScriptUtils Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source
* Copyright 2013, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.ajax4jsf.javascript;
import org.ajax4jsf.Messages;
import org.richfaces.log.Logger;
import org.richfaces.log.RichfacesLogger;
import javax.faces.FacesException;
import javax.faces.context.ResponseWriter;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.SimpleTimeZone;
/**
* @author shura (latest modification by $Author: alexsmirnov $)
* @version $Revision: 1.1.2.3 $ $Date: 2007/01/24 13:22:31 $
*/
public final class ScriptUtils {
private static final Logger LOG = RichfacesLogger.UTIL.getLogger();
// there is the same pattern in client-side code:
// richfaces.js - RichFaces.escapeCSSMetachars(s)
private static final char[] CSS_SELECTOR_CHARS_TO_ESCAPE = createSortedCharArray("#;&,.+*~':\"!^$[]()=>|/");
/**
* This is utility class, don't instantiate.
*/
private ScriptUtils() {
}
private static char[] createSortedCharArray(String s) {
char[] cs = s.toCharArray();
Arrays.sort(cs);
return cs;
}
private static void appendScript(Appendable appendable, Object obj, Map cycleBusterMap) throws IOException {
Boolean cycleBusterValue = cycleBusterMap.put(obj, Boolean.TRUE);
if (cycleBusterValue != null) {
if (LOG.isDebugEnabled()) {
String formattedMessage;
try {
formattedMessage = Messages.getMessage(Messages.JAVASCRIPT_CIRCULAR_REFERENCE, obj);
} catch (MissingResourceException e) {
// ignore exception: workaround for unit tests
formattedMessage = MessageFormat.format("Circular reference serializing object to JS: {0}", obj);
}
LOG.debug(formattedMessage);
}
appendable.append("null");
} else if (null == obj) {
// TODO nick - skip non-rendered values like Integer.MIN_VALUE
appendable.append("null");
} else if (obj instanceof ScriptString) {
((ScriptString) obj).appendScript(appendable);
} else if (obj.getClass().isArray()) {
appendable.append("[");
boolean first = true;
for (int i = 0; i < Array.getLength(obj); i++) {
Object element = Array.get(obj, i);
if (!first) {
appendable.append(',');
}
appendScript(appendable, element, cycleBusterMap);
first = false;
}
appendable.append("] ");
} else if (obj instanceof Collection>) {
// Collections put as JavaScript array.
@SuppressWarnings("unchecked")
Collection collection = (Collection) obj;
appendable.append("[");
boolean first = true;
for (Iterator iter = collection.iterator(); iter.hasNext();) {
Object element = iter.next();
if (!first) {
appendable.append(',');
}
appendScript(appendable, element, cycleBusterMap);
first = false;
}
appendable.append("] ");
} else if (obj instanceof Map, ?>) {
// Maps put as JavaScript hash.
@SuppressWarnings("unchecked")
Map map = (Map) obj;
appendable.append("{");
boolean first = true;
for (Map.Entry entry : map.entrySet()) {
if (!first) {
appendable.append(',');
}
appendEncodedString(appendable, entry.getKey());
appendable.append(":");
appendScript(appendable, entry.getValue(), cycleBusterMap);
first = false;
}
appendable.append("} ");
} else if (obj instanceof Number || obj instanceof Boolean) {
// numbers and boolean put as-is, without conversion
appendable.append(obj.toString());
} else if (obj instanceof String) {
// all other put as encoded strings.
appendEncodedString(appendable, obj);
} else if (obj instanceof Character) {
appendEncodedString(appendable, obj);
} else if (obj instanceof Enum>) {
// all other put as encoded strings.
appendEncodedString(appendable, obj);
} else {
// All other objects threaded as Java Beans.
appendable.append("{");
PropertyDescriptor[] propertyDescriptors;
try {
propertyDescriptors = PropertyUtils.getPropertyDescriptors(obj);
} catch (Exception e) {
throw new FacesException("Error in conversion Java Object to JavaScript", e);
}
boolean ignorePropertyReadException = obj.getClass().getName().startsWith("java.sql.")
|| obj.getClass().equals(SimpleTimeZone.class);
boolean first = true;
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
String key = propertyDescriptor.getName();
if ("class".equals(key)) {
continue;
}
Object propertyValue;
try {
propertyValue = PropertyUtils.readPropertyValue(obj, propertyDescriptor);
} catch (Exception e) {
if (!ignorePropertyReadException) {
throw new FacesException("Error in conversion Java Object to JavaScript", e);
} else {
continue;
}
}
if (!first) {
appendable.append(',');
}
appendEncodedString(appendable, key);
appendable.append(":");
appendScript(appendable, propertyValue, cycleBusterMap);
first = false;
}
appendable.append("} ");
}
if (cycleBusterValue == null) {
cycleBusterMap.remove(obj);
}
}
/**
* Convert any Java Object to JavaScript representation ( as possible ) and write it to writer immediately
*
* @param responseWriter
* @param obj
* @throws IOException
*/
public static void writeToStream(final ResponseWriter responseWriter, Object obj) throws IOException {
appendScript(new ResponseWriterWrapper(responseWriter), obj, new IdentityHashMap());
}
/**
* Convert any Java Object to JavaScript representation ( as possible ).
*
* @param obj
* @return Java Object converted to JavaScript representation
*/
public static String toScript(Object obj) {
StringBuilder sb = new StringBuilder();
if (obj instanceof String)
sb.ensureCapacity((int)(((String) obj).length() * 1.66));
try {
appendScript(sb, obj, new IdentityHashMap());
} catch (IOException e) {
// ignore
}
return sb.toString();
}
public static void appendScript(Appendable appendable, Object obj) throws IOException {
appendScript(appendable, obj, new IdentityHashMap());
}
public static void appendEncodedString(Appendable appendable, Object obj) throws IOException {
appendable.append("\"");
appendEncoded(appendable, obj);
appendable.append("\"");
}
public static void appendEncoded(Appendable appendable, Object obj) throws IOException {
String s = obj.toString();
int start = 0;
int end = s.length();
for(int i = start; i < end; i++){
char c = s.charAt(i);
if (JSEncoder.compile(c))
continue;
if (start != i)
appendable.append(s, start, i);
appendable.append(JSEncoder.encodeCharBuffer(c));
start = i + 1;
}
if (start != end)
appendable.append(s, start, end);
}
public static String getValidJavascriptName(String script) {
String s = "av_" + getMD5scriptHash(script);
StringBuilder sb = null;
final int len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (Character.isLetterOrDigit(c) || c == '_') {
// allowed char
if (sb != null) {
sb.append(c);
}
} else {
if (sb == null) {
sb = new StringBuilder(s.length() + 10);
sb.append(s.substring(0, i));
}
sb.append('_');
if (c < 16) {
// pad single hex digit values with '0' on the left
sb.append('0');
}
if (c < 128) {
// first 128 chars match their byte representation in UTF-8
sb.append(Integer.toHexString(c).toUpperCase());
} else {
byte[] bytes = Character.toString(c).getBytes(StandardCharsets.UTF_8);
for (int j = 0; j < bytes.length; j++) {
int intVal = bytes[j];
if (intVal < 0) {
// intVal will be >= 128
intVal = 256 + intVal;
} else if (intVal < 16) {
// pad single hex digit values with '0' on the left
sb.append('0');
}
sb.append(Integer.toHexString(intVal).toUpperCase());
}
}
}
}
return sb == null ? s : sb.toString();
}
public static String getMD5scriptHash(String script) {
byte[] bytesOfScript = script.getBytes(StandardCharsets.UTF_8);
MessageDigest md = null;
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unable to locate MD5 hash algorithm", e);
}
md.reset();
//byte[] thedigest = md.digest(bytesOfScript);
md.update(bytesOfScript);
byte[] messageDigest = md.digest();
StringBuilder hexString = new StringBuilder();
for (int i = 0; i < messageDigest.length; i++) {
String hex = Integer.toHexString(0xFF & messageDigest[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
public static boolean shouldRenderAttribute(Object attributeVal) {
if (null == attributeVal) {
return false;
} else if (attributeVal instanceof Boolean && ((Boolean) attributeVal).booleanValue() == Boolean.FALSE.booleanValue()) {
return false;
} else if (attributeVal.toString().length() == 0) {
return false;
} else {
return isValidProperty(attributeVal);
}
}
public static boolean shouldRenderAttribute(String attributeName, Object attributeVal) {
return shouldRenderAttribute(attributeVal);
}
/**
* Test for valid value of property. by default, for non-setted properties with Java primitive types of JSF component return
* appropriate MIN_VALUE .
*
* @param property - value of property returned from {@link javax.faces.component.UIComponent#getAttributes()}
* @return true for setted property, false otherthise.
*/
public static boolean isValidProperty(Object property) {
if (null == property) {
return false;
} else if (property instanceof Integer && ((Integer) property).intValue() == Integer.MIN_VALUE) {
return false;
} else if (property instanceof Double && ((Double) property).doubleValue() == Double.MIN_VALUE) {
return false;
} else if (property instanceof Character && ((Character) property).charValue() == Character.MIN_VALUE) {
return false;
} else if (property instanceof Float && ((Float) property).floatValue() == Float.MIN_VALUE) {
return false;
} else if (property instanceof Short && ((Short) property).shortValue() == Short.MIN_VALUE) {
return false;
} else if (property instanceof Byte && ((Byte) property).byteValue() == Byte.MIN_VALUE) {
return false;
} else if (property instanceof Long && ((Long) property).longValue() == Long.MIN_VALUE) {
return false;
}
return true;
}
/**
*
* Escapes CSS meta-characters in string according to jQuery
* selectors document.
*
*
* @param s {@link String} to escape meta-characters in
* @return string with escaped characters.
*/
public static String escapeCSSMetachars(String s) {
if (s == null || s.length() == 0) {
return s;
}
StringBuilder builder = new StringBuilder();
int start = 0;
int idx = 0;
int length = s.length();
for (; idx < length; idx++) {
char c = s.charAt(idx);
int searchIdx = Arrays.binarySearch(CSS_SELECTOR_CHARS_TO_ESCAPE, c);
if (searchIdx >= 0) {
builder.append(s.substring(start, idx));
builder.append("\\");
builder.append(c);
start = idx + 1;
}
}
builder.append(s.substring(start, idx));
return builder.toString();
}
}