package com.eas.core; import java.util.Date; import java.util.Map; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArrayString; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.RepeatingCommand; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Document; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.xml.client.Element; import com.google.gwt.xml.client.NamedNodeMap; import com.google.gwt.xml.client.Node; import com.google.gwt.xml.client.NodeList; public class Utils { public static JavaScriptObject checkPublishedComponent(Object aCandidate) { if (aCandidate instanceof HasPublished) { return ((HasPublished) aCandidate).getPublished(); } else return null; } public static class JsObject extends JavaScriptObject { protected JsObject() { } public static native String formatDateValueWithJSON(double aDateTimeValue)/*-{ return JSON.stringify(new Date(aDateTimeValue)); }-*/; public final native Object getJava(String aName)/*-{ var B = @com.eas.core.Predefine::boxing; return B.boxAsJava(this[aName]); }-*/; public final native boolean getBoolean(String aName)/*-{ return !!this[aName]; }-*/; public final native String getString(String aName)/*-{ var v = this[aName]; return v != null ? v + '' : null; }-*/; public final native String getString(int aIndex)/*-{ var v = this[aIndex]; return v != null ? v + '' : null; }-*/; public final native int getInteger(String aName)/*-{ return +this[aName]; }-*/; public final native void setInteger(String aName, int aValue)/*-{ this[aName] = aValue; }-*/; public final native double getDouble(String aName)/*-{ return +this[aName]; }-*/; public final native Utils.JsObject getJs(String aName)/*-{ return this[aName]; }-*/; public final native void setJs(String aName, JavaScriptObject aValue)/*-{ return this[aName] = aValue; }-*/; public final native void setJava(String aName, Object aValue)/*-{ var B = @com.eas.core.Predefine::boxing; return this[aName] = B.boxAsJs(aValue); }-*/; public final native boolean has(String aName)/*-{ return typeof this[aName] != 'undefined'; }-*/; public final native JavaScriptObject deleteProperty(String aName)/*-{ delete this[aName]; }-*/; public final native void defineProperty(String aName, JavaScriptObject aDefinition)/*-{ Object.defineProperty(this, aName, aDefinition); }-*/; public final void inject(String aName, JavaScriptObject aValue) { inject(aName, aValue, false); } public final native void inject(String aName, JavaScriptObject aValue, boolean aConfigurable)/*-{ if (aName != null) { Object.defineProperty(this, aName, { configurable : aConfigurable, get : function() { return aValue; } }); } }-*/; public final native void inject(String aName, JavaScriptObject aValue, boolean aEnumerable, boolean aConfigurable)/*-{ if (aName != null) { Object.defineProperty(this, aName, { enumerable : aEnumerable, configurable : aConfigurable, get : function() { return aValue; } }); } }-*/; public final native boolean isArray()/*-{ return Array.isArray(this); }-*/; public final native int length()/*-{ return this.length; }-*/; public final native JavaScriptObject splice(int aIndex, int howManyToDelete, JavaScriptObject toInsert)/*-{ return this.splice(aIndex, howManyToDelete, toInsert); }-*/; public final native JavaScriptObject splice(int aIndex, int howManyToDelete)/*-{ return this.splice(aIndex, howManyToDelete); }-*/; public final native JsArrayString keys()/*-{ return Object.keys(this); }-*/; public final native String getStringSlot(int i)/*-{ return this[i]; }-*/; public final native JavaScriptObject getSlot(int i)/*-{ return this[i]; }-*/; public final native void setSlot(int i, JavaScriptObject aValue)/*-{ this[i] = aValue; }-*/; public final native void setSlot(int i, int aValue)/*-{ this[i] = aValue; }-*/; public final native void setSlot(int i, String aValue)/*-{ this[i] = aValue; }-*/; public final native void setSlot(int i, boolean aValue)/*-{ this[i] = aValue; }-*/; public final native Object apply(JavaScriptObject aThis, JavaScriptObject aArgs)/*-{ return this.apply(aThis, aArgs != null ? aArgs : []); }-*/; public final native Object call(JavaScriptObject aThis, Object aArg)/*-{ var B = @com.eas.core.Predefine::boxing; return this.call(aThis, B.boxAsJs(aArg)); }-*/; public final native Object call(JavaScriptObject aThis, Object aArg, boolean aFlag)/*-{ var B = @com.eas.core.Predefine::boxing; return this.call(aThis, B.boxAsJs(aArg), aFlag); }-*/; public final native JavaScriptObject newObject()/*-{ var constr = this; return new constr(); }-*/; public static native JavaScriptObject dateReviver()/*-{ return function(k, v) { if (typeof v === 'string' && /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/.test(v)) { return new $wnd.Date(v); } else { return v; } }; }-*/; public static native JavaScriptObject parseJSON(String aText)/*-{ return $wnd.JSON.parse(aText); }-*/; public static native String stringifyJSON(Object aValue)/*-{ return $wnd.JSON.stringify(aValue); }-*/; public static native JavaScriptObject parseJSONDateReviver(String aText)/*-{ return $wnd.JSON.parse(aText, @com.eas.core.Utils.JsObject::dateReviver()()); }-*/; public static native String writeJSON(JavaScriptObject changeLog)/*-{ return $wnd.JSON.stringify(changeLog); }-*/; public native final int indexOf(JavaScriptObject anElement)/*-{ return this.indexOf(anElement); }-*/; } private static final String addListenerName = "-platypus-listener-add-func"; private static final String removeListenerName = "-platypus-listener-remove-func"; private static final String fireChangeName = "-platypus-change-fire-func"; public static native JavaScriptObject listenable(JavaScriptObject aTarget)/*-{ var listeners = new Set(); Object.defineProperty(aTarget, @com.eas.core.Utils::addListenerName, { value : function(aListener) { listeners.add(aListener); } }); Object.defineProperty(aTarget, @com.eas.core.Utils::removeListenerName, { value : function(aListener) { listeners['delete'](aListener); } }); Object.defineProperty(aTarget, @com.eas.core.Utils::fireChangeName, { value : function(aChange) { Object.freeze(aChange); var _listeners = []; listeners.forEach(function(aListener) { _listeners.push(aListener); }); _listeners.forEach(function(aListener) { aListener(aChange); }); } }); return function() { unlistenable(aTarget); }; }-*/; public static native void unlistenable(JavaScriptObject aTarget)/*-{ delete aTarget[@com.eas.core.Utils::addListenerName]; delete aTarget[@com.eas.core.Utils::removeListenerName]; }-*/; public static native JavaScriptObject listen(JavaScriptObject aTarget, JavaScriptObject aListener)/*-{ var addListener = aTarget[@com.eas.core.Utils::addListenerName]; if (addListener) { addListener(aListener); return function() { aTarget[@com.eas.core.Utils::removeListenerName](aListener); }; } else { return null; } }-*/; public static native void unlisten(JavaScriptObject aTarget, JavaScriptObject aListener)/*-{ aTarget[@com.eas.core.Utils::removeListenerName](aListener); }-*/; public static native void fire(JavaScriptObject aTarget, JavaScriptObject aChange)/*-{ try { aTarget[@com.eas.core.Utils::fireChangeName](aChange); } catch (e) { var Logger = @com.eas.core.Predefine::logger; Logger.Logger.severe(e); } }-*/; public static native JavaScriptObject publishCancellable(Cancellable aValue)/*-{ return { abort : function() { aValue.@com.eas.core.Cancellable::cancel()(); } }; }-*/; public static native JavaScriptObject publishRunnable(Runnable aValue)/*-{ return (function() { aValue.@java.lang.Runnable::run()(); }); }-*/; public interface OnChangeHandler { public void onChange(JavaScriptObject anEvent); } public static native JavaScriptObject publishOnChangeHandler(OnChangeHandler aValue)/*-{ return function(aArg) { aValue.@com.eas.core.Utils.OnChangeHandler::onChange(Lcom/google/gwt/core/client/JavaScriptObject;)(aArg); }; }-*/; public static native JavaScriptObject stringToArrayBuffer(String aValue) throws Exception/*-{ if (aValue) { var buffer = new ArrayBuffer(aValue.length); var bufferView = new Uint8Array(buffer); for ( var i = 0; i < aValue.length; i++) bufferView[i] = aValue.charCodeAt(i); return buffer; } else return null; }-*/; public static void invokeLater(final JavaScriptObject aTarget) { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { invokeJsFunction(aTarget); } }); } public static void invokeScheduled(int aTimeout, final JavaScriptObject aTarget) { Scheduler.get().scheduleFixedDelay(new RepeatingCommand() { @Override public boolean execute() { invokeJsFunction(aTarget); return false; } }, aTimeout); } public static String format(final String format, final String... args) { String[] split = format.split("%s"); final StringBuffer msg = new StringBuffer(); for (int pos = 0; pos < split.length - 1; pos += 1) { msg.append(split[pos]); msg.append(args[pos]); } msg.append(split[split.length - 1]); return msg.toString(); } public static final Document doc = Document.get(); public native static Boolean executeScriptEventBoolean(JavaScriptObject aEventThis, JavaScriptObject aHandler, Object aArg) throws Exception/*-{ if (aHandler != null) { var B = @com.eas.core.Predefine::boxing; var res = aHandler.call(aEventThis, B.boxAsJs(aArg)); if (res != undefined && res != null) return @java.lang.Boolean::new(Z)((false != res)); else return null; } else return null; }-*/; public native static Double executeScriptEventDouble(JavaScriptObject aEventThis, JavaScriptObject aHandler, Object aArg) throws Exception/*-{ if (aHandler != null) { var B = @com.eas.core.Predefine::boxing; var res = aHandler.call(aEventThis, B.boxAsJs(aArg)); if (res != undefined && res != null) return @java.lang.Double::new(D)(res * 1); else return null; } else return null; }-*/; public native static String executeScriptEventString(JavaScriptObject aEventThis, JavaScriptObject aHandler, Object aArg) throws Exception/*-{ if (aHandler != null) { var B = @com.eas.core.Predefine::boxing; var res = aHandler.call(aEventThis, B.boxAsJs(aArg)); if (res != undefined && res != null) return (res + ''); else return null; } else return null; }-*/; public native static Date executeScriptEventDate(JavaScriptObject aEventThis, JavaScriptObject aHandler, Object aArg) throws Exception/*-{ if (aHandler != null) { var B = @com.eas.core.Predefine::boxing; var res = new Date(aHandler.call(aEventThis, B.boxAsJs(aArg))); if (res != undefined && res != null) return @com.eas.core.Utils::double2Date(D)(res.getTime()); else return null; } else return null; }-*/; public native static void executeScriptEventVoid(JavaScriptObject aEventThis, JavaScriptObject aHandler, Object aArg) throws Exception/*-{ if (aHandler != null) { var B = @com.eas.core.Predefine::boxing; aHandler.call(aEventThis, B.boxAsJs(aArg)); } }-*/; public native static Object jsonParse(String aData) throws Exception /*-{ var B = @com.eas.core.Predefine::boxing; return B.boxAsJava($wnd.JSON.parse(aData, @com.eas.core.Utils.JsObject::dateReviver()())); }-*/; public native static String jsonStringify(Object aToJsedObject) /*-{ var B = @com.eas.core.Predefine::boxing; return $wnd.JSON.stringify(B.boxAsJs(aToJsedObject)); }-*/; public native static void invokeJsFunction(JavaScriptObject aHandler) /*-{ if (aHandler) { aHandler(); } }-*/; public static boolean isDate(Object aValue) { return aValue instanceof Date; } public static Date double2Date(double aTime) { return new Date(Double.valueOf(aTime).longValue()); } public static double date2Double(Date aDate) { return Long.valueOf(aDate.getTime()).doubleValue(); } public static native JavaScriptObject toScriptDate(Number aValue)/*-{ if (aValue == null) return null; else return new Date(aValue.@java.lang.Number::doubleValue()()); }-*/; public static Object toJs(Object aValue) { if (aValue instanceof Number) return ((Number) aValue).doubleValue(); else if (aValue instanceof Date) return toScriptDate(Long.valueOf(((Date) aValue).getTime())); else if (aValue instanceof String || aValue instanceof Boolean) return aValue; else return aValue; } public static native Object unwrap(JavaScriptObject aValue)/*-{ if (aValue == null || aValue == undefined) return null; else if (aValue instanceof Date || aValue instanceof $wnd.Date || aValue.constructor.name == "Date") return @com.eas.core.Utils::double2Date(D)(aValue.getTime()); else if(aValue instanceof Boolean || aValue instanceof $wnd.Boolean || aValue.constructor.name == "Boolean") return @java.lang.Boolean::new(Z)(aValue == true); else if(aValue instanceof String || aValue instanceof $wnd.String || aValue.constructor.name == "String") return @java.lang.String::new(Ljava/lang/String;)(aValue + ''); else if(!isNaN(aValue) || !$wnd.isNaN(aValue)) return @java.lang.Double::new(D)(aValue * 1); else return aValue; }-*/; public static Object toJava(Object aValue) { if (aValue instanceof JavaScriptObject) return unwrap((JavaScriptObject) aValue); else if (aValue instanceof Number || aValue instanceof Boolean || aValue instanceof String || aValue instanceof Date) return aValue; else return null; } public static native boolean isMobile()/*-{ return $wnd.orientation !== undefined; }-*/; /* * public static Element getElementByTagName(Element aTag, String aName) { * NodeList children = aTag.getElementsByTagName(aName); if * (children.getLength() == 1) { if (children.item(0) instanceof Element) * return (Element) children.item(0); else throw new * IllegalStateException("XML node '" + aName + "' must be a tag node"); } * else if (children.getLength() > 1) throw new * IllegalStateException("Tag '" + aName + "' must be only one"); return * null; } */ public static Element scanForElementByTagName(Element aTag, String aShortName, String aLongName) { NodeList nl = aTag.getChildNodes(); if (nl != null) { for (int i = 0; i < nl.getLength(); i++) { Node n = nl.item(i); if (n.getNodeName().equals(aShortName) || n.getNodeName().equals(aLongName)) { if (n instanceof Element) return (Element) n; else throw new IllegalStateException(n.getNodeName() + " must be a tag node."); } } } return null; } public static Float getFloatAttribute(Element aElement, String aShortName, String aLongName, Float defaultValue) { String attrValue = getAttribute(aElement, aShortName, aLongName, null); return attrValue != null && !"null".equals(attrValue) ? Float.valueOf(attrValue) : defaultValue; } public static Double getDoubleAttribute(Element aElement, String aShortName, String aLongName, Double defaultValue) { String attrValue = getAttribute(aElement, aShortName, aLongName, null); return attrValue != null && !"null".equals(attrValue) ? Double.valueOf(attrValue) : defaultValue; } public static Integer getIntegerAttribute(Element aElement, String aShortName, String aLongName, Integer defaultValue) { String attrValue = getAttribute(aElement, aShortName, aLongName, null); return attrValue != null && !"null".equals(attrValue) ? Integer.valueOf(attrValue) : defaultValue; } public static Boolean getBooleanAttribute(Element aElement, String aShortName, String aLongName, Boolean defaultValue) { String attrValue = getAttribute(aElement, aShortName, aLongName, null); return attrValue != null && !"null".equals(attrValue) ? Boolean.valueOf(attrValue) : defaultValue; } public static boolean hasAttribute(Element anElement, String aShortName, String aLongName) { return anElement.hasAttribute(aShortName) ? true : anElement.hasAttribute(aLongName); } public static String getAttribute(Element aElement, String aShortName, String aLongName, String defaultValue) { NamedNodeMap attrs = aElement.getAttributes(); Node a = attrs.getNamedItem(aShortName); if (a == null) { a = attrs.getNamedItem(aLongName); } return a != null ? a.getNodeValue() : defaultValue; } public static int getPxAttribute(Element aElement, String aShortName, String aLongName, int aDefValue) throws Exception { String attrValue = getAttribute(aElement, aShortName, aLongName, null); if (attrValue != null && !"null".equals(attrValue)) { if (attrValue.endsWith("px")) attrValue = attrValue.substring(0, attrValue.length() - 2); return Integer.valueOf(attrValue); } else { return aDefValue; } } public static int javaArrayLength(Object[] aValue) { return aValue.length; } public static Object javaArrayItem(Object[] aValue, int aIndex) { return aValue[aIndex]; } /** * Turns a java array into js array. * * @param aValue * Array with prepared items already processed with Utils.toJs(). * @return js array. */ public native static JavaScriptObject toJsArray(Object[] aValue)/*-{ var B = @com.eas.core.Predefine::boxing; var res = []; var inLength = @com.eas.core.Utils::javaArrayLength([Ljava/lang/Object;)(aValue); for ( var i = 0; i < inLength; i++) { res[res.length] = B.boxAsJs(@com.eas.core.Utils::javaArrayItem([Ljava/lang/Object;I)(aValue, i)); } return res; }-*/; public static native Map<String, String> extractSimpleProperties(JavaScriptObject aObject) throws Exception /*-{ var m = @java.util.HashMap::new()(); for (var key in aObject) { var val = aObject[key]; if (!(val instanceof Object) || val instanceof Date || val instanceof Number || val instanceof String || val instanceof Boolean) { m.@java.util.HashMap::put(Ljava/lang/Object;Ljava/lang/Object;)(key, $wnd.JSON.stringify(val)); } } return m; }-*/; public static native Object getPathData(JavaScriptObject anElement, String aPath)/*-{ if (anElement != null && aPath != null && aPath != '') { var target = anElement; var path = aPath.split('.'); var propName = path[0]; for ( var i = 1; i < path.length; i++) { var target = target[propName]; if (!target) { propName = null; } else propName = path[i]; } if (propName != null) { var B = @com.eas.core.Predefine::boxing; var javaValue = B.boxAsJava(target[propName]); return javaValue; } else return null; } else return null; }-*/; public static native void setPathData(JavaScriptObject anElement, String aPath, Object aValue)/*-{ if (aPath != null && aPath != '') { var target = anElement; var path = aPath.split('.'); var propName = path[0]; for ( var i = 1; i < path.length; i++) { var target = target[propName]; if (!target) { propName = null; } else { propName = path[i]; } } if (propName != null) { var B = @com.eas.core.Predefine::boxing; var jsData = B.boxAsJs(aValue); target[propName] = jsData; } } }-*/; private static native JavaScriptObject observeElements(JavaScriptObject aTarget, JavaScriptObject aPropListener)/*-{ function subscribe(aData, aListener) { var nHandler = @com.eas.core.Utils::listen(Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)(aData, aListener); if (nHandler != null) { return nHandler; } else if ($wnd.Object.observe) { var observer = function(changes) { aListener(); }; if (Array.isArray(aData)) { Array.observe(aData, observer); return function() { Array.unobserve(aData, observer); }; } else { Object.observe(aData, observer); return function() { Object.unobserve(aData, observer); }; } } return null; } var subscribed = []; for ( var i = 0; i < aTarget.length; i++) { var remover = subscribe(aTarget[i], aPropListener); if (remover) { subscribed.push(remover); } } return { unlisten : function() { subscribed.forEach(function(aEntry) { aEntry(); }); } }; }-*/; private static native JavaScriptObject observePath(JavaScriptObject aTarget, String aPath, JavaScriptObject aPropListener)/*-{ function subscribe(aData, aListener, aPropName) { var nHandler = @com.eas.core.Utils::listen(Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)(aData, function(aChange){ if(!aPropName || aChange.propertyName == aPropName){ aListener(aChange); } }); if(nHandler != null){ return nHandler; }else if($wnd.Object.observe){ var observer = function(changes){ var touched = false; changes.forEach(function(change){ if(change.name == aPropName || (change.type == 'splice' && aPropName == 'length')) touched = true; }); if(touched){ aListener(typeof aData[aPropName] != 'undefined' ? aData[aPropName] : null); } }; if(Array.isArray(aData)){ Array.observe(aData, observer); return function (){ Array.unobserve(aData, observer); }; }else{ Object.observe(aData, observer); return function (){ Object.unobserve(aData, observer); }; } } return null; } var subscribed = []; function listenPath() { subscribed = []; var data = aTarget; var path = aPath.split('.'); for (var i = 0; i < path.length; i++) { var propName = path[i]; var listener = i === path.length - 1 ? aPropListener : function (evt) { subscribed.forEach(function (aEntry) { aEntry(); }); listenPath(); aPropListener(evt); }; var cookie = subscribe(data, listener, propName); if (cookie) { subscribed.push(cookie); if (data[propName]) data = data[propName]; else break; } else { break; } } } if (aTarget) { listenPath(); } return { unlisten: function () { subscribed.forEach(function (aEntry) { aEntry(); }); } }; }-*/; public static HandlerRegistration listenPath(JavaScriptObject anElement, String aPath, OnChangeHandler aListener) { final JsObject listener = observePath(anElement, aPath, publishOnChangeHandler(aListener)).cast(); return new HandlerRegistration() { @Override public void removeHandler() { JsObject unlisten = listener.getJs("unlisten").cast(); unlisten.apply(listener, null); } }; } public static HandlerRegistration listenElements(JavaScriptObject anElements, OnChangeHandler aListener) { final JsObject listener = observeElements(anElements, publishOnChangeHandler(aListener)).cast(); return new HandlerRegistration() { @Override public void removeHandler() { JsObject unlisten = listener.getJs("unlisten").cast(); unlisten.apply(listener, null); } }; } private static String extractFileName(StackTraceElement aFrame) { String fileName = aFrame.getFileName(); if (fileName != null) { int atIndex = fileName.indexOf("@"); if (atIndex != -1) { fileName = fileName.substring(0, atIndex); } return fileName; } else { return null; } } public static String lookupCallerJsDir() { String calledFromFile = lookupCallerJsFile(); if (calledFromFile != null) { int lastSlashIndex = calledFromFile.lastIndexOf('/'); return calledFromFile.substring(0, lastSlashIndex); } else { return null; } } public static String lookupCallerJsFile() { String calledFromFile = null; try { throw new Exception("Current file test"); } catch (Exception ex) { StackTraceElement[] stackFrames = ex.getStackTrace(); String firstFileName = extractFileName(stackFrames[0]); if (firstFileName != null) { for (int frameIdx = 1; frameIdx < stackFrames.length; frameIdx++) { String fileName = extractFileName(stackFrames[frameIdx]); if (fileName != null && !fileName.equals(firstFileName)) { calledFromFile = fileName; int lastQuestionIndex = calledFromFile.lastIndexOf('?');// case of cache busting return lastQuestionIndex != -1 ? calledFromFile.substring(0, lastQuestionIndex) : calledFromFile; } } } } return calledFromFile; } }