/* * � Copyright IBM Corp. 2010, 2013 * * Licensed 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 com.ibm.xsp.extlib.util; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Enumeration; import java.util.Map; import javax.faces.component.NamingContainer; import javax.faces.component.UIComponent; import javax.faces.component.UIViewRoot; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.faces.el.ValueBinding; import lotus.domino.Database; import lotus.domino.Session; import org.osgi.framework.Bundle; import com.ibm.commons.Platform; import com.ibm.commons.util.StringUtil; import com.ibm.domino.xsp.module.nsf.NotesContext; import com.ibm.xsp.FacesExceptionEx; import com.ibm.xsp.ajax.AjaxUtil; import com.ibm.xsp.application.ApplicationEx; import com.ibm.xsp.application.UniqueViewIdManager; import com.ibm.xsp.binding.ComponentBindingObject; import com.ibm.xsp.component.FacesNestedDataTable; import com.ibm.xsp.component.FacesRefreshableComponent; import com.ibm.xsp.component.UIScriptCollector; import com.ibm.xsp.component.UIViewRootEx; import com.ibm.xsp.core.Version; import com.ibm.xsp.designer.context.XSPContext; import com.ibm.xsp.extlib.plugin.ExtLibCoreActivator; import com.ibm.xsp.resource.ScriptResource; import com.ibm.xsp.util.FacesUtil; import com.ibm.xsp.util.TypedUtil; /** * General purposes XPages utilities. */ public class ExtLibUtil { // ============================================================================== // Development mode // ============================================================================== private static boolean DVLP_MODE; static { if(isXPages900()) { // Prepare for 900 try { Class<?> c = Class.forName("com.ibm.xsp.util.XspUtil");//$NON-NLS-1$ DVLP_MODE = (Boolean)c.getMethod("isDevelopmentMode").invoke(null); //$NON-NLS-1$ } catch(Throwable t) { // Force it to false... DVLP_MODE = false; } } else { DVLP_MODE = false; try { // Look for a flag String prop = Platform.getInstance().getProperty("xsp.extlib.dvlp"); // $NON-NLS-1$ if(StringUtil.isEmpty(prop)) { // Look for a system property prop = System.getProperty("xsp.extlib.dvlp"); // $NON-NLS-1$ if(StringUtil.isEmpty(prop)) { // Look for a Notes.ini property // The Backend classes object is not yet available when this gets initialized, so // we should call the native code here. // To avoid any dependency from this to the native layer, we use Java reflection. Note that // this underlying code can change at any time, so it might silently fail in the future prop = com.ibm.xsp.model.domino.DominoUtils.getEnvironmentString("XPagesDev"); // $NON-NLS-1$ } } if(StringUtil.isNotEmpty(prop)) { if(StringUtil.equals(prop,"true") || StringUtil.equals(prop,"1")) { // $NON-NLS-1$ DVLP_MODE = true; } } } catch(Throwable t) { //Platform.getInstance().log(t); // Ok, assume not dev mode DVLP_MODE = false; } if(DVLP_MODE) { System.out.println("XPages is running in development mode, resulting in decreased performance"); // $NON-NLS-1$ } } } /** * Check if debug mode should be applied. */ public static final boolean isDevelopmentMode() { return DVLP_MODE; } public static final boolean isDebugJavaScript() { return false; } /** * Check for the runtime version. */ public static final boolean isXPages852() { if(_852==null) { Version v = Version.CurrentRuntimeVersion; _852 = (v.getMajor()==8 && v.getMinor()==5 && v.getMicro()==2); } return _852; } private static Boolean _852; public static final boolean isXPages853() { if(_853==null) { Version v = Version.CurrentRuntimeVersion; _853 = (v.getMajor()==8 && v.getMinor()==5 && v.getMicro()==3); } return _853; } private static Boolean _853; /** * @deprecated */ public static final boolean isXPages854() { throw new UnsupportedOperationException( "Unsupported isXPages854() method. Please use isXPages900() instead." // $NON-NLS-1$ ); } public static final boolean isXPages900() { if(_900==null) { Version v = Version.CurrentRuntimeVersion; _900 = (v.getMajor()==9 && v.getMinor()==0 && v.getMicro()==0); } return _900; } private static Boolean _900; /** * Check if a class is loaded from the application or is global (part of the runtime) * @return */ public static final boolean isApplicationClass(Class<?> c) { //TODO 9.0 should have the dynamic classloader implementing ApplicationClassLoader ClassLoader cl = c.getClassLoader(); return cl.getClass().getName().indexOf("DynamicClassLoader")>=0; // $NON-NLS-1$ } // ============================================================================== // ExtLib version // ============================================================================== public static String getExtLibVersion() { try { String s = AccessController.doPrivileged(new PrivilegedAction<String>() { public String run() { Object o = ExtLibCoreActivator.instance.getBundle().getHeaders().get("Bundle-Version"); // $NON-NLS-1$ if(o!=null) { return o.toString(); } return null; } }); if(s!=null) { return s; } } catch(SecurityException ex) {} return ""; } // ============================================================================== // XspContext access // ============================================================================== /** * Return the current XspContext. */ public static XSPContext getXspContext() { return XSPContext.getXSPContext(FacesContext.getCurrentInstance()); } //PR35 - Erik McCormick //https://github.com/OpenNTF/XPagesExtensionLibrary/pull/35/files /** * Returns a XSP property value */ public static String getXspProperty(String name) { XSPContext context = getXspContext(); if(context!=null) return context.getProperty(name); else return null; } /** * Resolve the specified variable. */ public static Object resolveVariable(FacesContext facesContext, String name) { Object value = facesContext.getApplication().getVariableResolver().resolveVariable(facesContext, name); return value; } /** * Resolve the specified variable using the current {@link FacesContext} instance. */ public static Object resolveVariable(String name) { // Note calling getCurrentInstance may have performance issues due to the multi thread handling. // method contributed bt Jesse Gallagher // https://github.com/OpenNTF/XPagesExtensionLibrary/pull/27 return resolveVariable(FacesContext.getCurrentInstance(), name); } /** * Return the compositeData map for the current component. */ @SuppressWarnings("unchecked") // $NON-NLS-1$ public static Map<String,Object> getCompositeData(FacesContext ctx) { if(ctx!=null) { return (Map<String,Object>)ctx.getApplication().getVariableResolver().resolveVariable(ctx, "compositeData"); // $NON-NLS-1$ } return null; } @SuppressWarnings("unchecked") // $NON-NLS-1$ public static Map<String,Object> getCompositeData() { FacesContext ctx = FacesContext.getCurrentInstance(); if(ctx!=null) { return (Map<String,Object>)ctx.getApplication().getVariableResolver().resolveVariable(ctx, "compositeData"); // $NON-NLS-1$ } return null; } /** * Return the current database. */ public static Database getCurrentDatabase(FacesContext ctx) { return getCurrentDatabase(); } public static Database getCurrentDatabase() { // WARN: The API bellow can change so do not use it directly! NotesContext nc = NotesContext.getCurrentUnchecked(); return nc!=null ? nc.getCurrentDatabase() : null; } /** * Return the current session. */ public static Session getCurrentSession(FacesContext ctx) { return getCurrentSession(); } public static Session getCurrentSession() { // WARN: The API bellow can change so do not use it directly! NotesContext nc = NotesContext.getCurrentUnchecked(); return nc!=null ? nc.getCurrentSession() : null; } /** * Return the signer session. */ public static Session getCurrentSessionAsSigner(FacesContext ctx) { return getCurrentSessionAsSigner(); } public static Session getCurrentSessionAsSigner() { // WARN: The API bellow can change so do not use it directly! NotesContext nc = NotesContext.getCurrentUnchecked(); return nc!=null ? nc.getSessionAsSigner() : null; } /** * Return the signer session with full access. */ public static Session getCurrentSessionAsSignerWithFullAccess(FacesContext ctx) { return getCurrentSessionAsSigner(); } public static Session getCurrentSessionAsSignerWithFullAccess() { // WARN: The API bellow can change so do not use it directly! NotesContext nc = NotesContext.getCurrentUnchecked(); return nc!=null ? nc.getSessionAsSignerFullAdmin() : null; } /** * Return the applicationScope. */ @SuppressWarnings("unchecked") // $NON-NLS-1$ public static Map<String,Object> getApplicationScope(FacesContext ctx) { if(ctx!=null) { return ctx.getExternalContext().getApplicationMap(); } return null; } @SuppressWarnings("unchecked") // $NON-NLS-1$ public static Map<String,Object> getApplicationScope() { FacesContext ctx = FacesContext.getCurrentInstance(); if(ctx!=null) { return ctx.getExternalContext().getApplicationMap(); } return null; } /** * Return the sessionScope. */ @SuppressWarnings("unchecked") // $NON-NLS-1$ public static Map<String,Object> getSessionScope(FacesContext ctx) { if(ctx!=null) { return ctx.getExternalContext().getSessionMap(); } return null; } @SuppressWarnings("unchecked") // $NON-NLS-1$ public static Map<String,Object> getSessionScope() { FacesContext ctx = FacesContext.getCurrentInstance(); if(ctx!=null) { return ctx.getExternalContext().getSessionMap(); } return null; } /** * Return the requestScope. */ @SuppressWarnings("unchecked") // $NON-NLS-1$ public static Map<String,Object> getRequestScope(FacesContext ctx) { if(ctx!=null) { return ctx.getExternalContext().getRequestMap(); } return null; } @SuppressWarnings("unchecked") // $NON-NLS-1$ public static Map<String,Object> getRequestScope() { FacesContext ctx = FacesContext.getCurrentInstance(); if(ctx!=null) { return ctx.getExternalContext().getRequestMap(); } return null; } /** * Return the viewScope. */ @SuppressWarnings("unchecked") // $NON-NLS-1$ public static Map<String,Object> getViewScope(FacesContext ctx) { UIViewRoot root = ctx.getViewRoot(); if(root instanceof UIViewRootEx) { return ((UIViewRootEx)root).getViewMap(); } return null; } @SuppressWarnings("unchecked") // $NON-NLS-1$ public static Map<String,Object> getViewScope() { FacesContext ctx = FacesContext.getCurrentInstance(); UIViewRoot root = ctx.getViewRoot(); if(root instanceof UIViewRootEx) { return ((UIViewRootEx)root).getViewMap(); } return null; } public static UIViewRootEx getViewRoot() { return (UIViewRootEx) ExtLibUtil.resolveVariable("view"); // $NON-NLS-1$ } // ============================================================================== // Data conversion // ============================================================================== public static String asString(Object v, String defaultValue) { if(v!=null) { return v.toString(); } return defaultValue; } public static String asString(Object v) { return asString(v, null); } public static int asInteger(Object v, int defaultValue) { if(v!=null) { if(v instanceof Number) { return ((Number)v).intValue(); } if(v instanceof String) { return Integer.valueOf((String)v); } if(v instanceof Boolean) { return ((Boolean)v) ? 1 : 0; } } return defaultValue; } public static int asInteger(Object v) { return asInteger(v, 0); } public static long asLong(Object v, long defaultValue) { if(v!=null) { if(v instanceof Number) { return ((Number)v).longValue(); } if(v instanceof String) { return Long.valueOf((String)v); } if(v instanceof Boolean) { return ((Boolean)v) ? 1L : 0L; } } return defaultValue; } public static long asLong(Object v) { return asLong(v, 0); } public static double asDouble(Object v, double defaultValue) { if(v!=null) { if(v instanceof Number) { return ((Number)v).doubleValue(); } if(v instanceof String) { return Double.valueOf((String)v); } if(v instanceof Boolean) { return ((Boolean)v) ? 1.0 : 0.0; } } return defaultValue; } public static double asDouble(Object v) { return asDouble(v, 0); } public static boolean asBoolean(Object v, boolean defaultValue) { if(v!=null) { if(v instanceof Boolean) { return ((Boolean)v).booleanValue(); } if(v instanceof String) { return Boolean.valueOf((Boolean)v); } if(v instanceof Number) { return ((Number)v).intValue()!=0; } return true; } return defaultValue; } public static boolean asBoolean(Object v) { return asBoolean(v, false); } // ============================================================================== // Property access helpers // ============================================================================== public static String getStringProperty(XSPContext ctx, String propName, String defaultValue) { String v = ctx.getProperty(propName); if(v==null) { return defaultValue; } return v; } public static String getStringProperty(XSPContext ctx, String propName) { return getStringProperty(ctx, propName, null); } public static int getIntProperty(XSPContext ctx, String propName, int defaultValue) { String v = ctx.getProperty(propName); if(v==null) { return defaultValue; } return Integer.valueOf(v); } public static int getIntProperty(XSPContext ctx, String propName) { return getIntProperty(ctx, propName, 0); } public static boolean getBooleanProperty(XSPContext ctx, String propName, boolean defaultValue) { String v = ctx.getProperty(propName); if(v==null) { return defaultValue; } return Boolean.valueOf(v); } public static boolean getBooleanProperty(XSPContext ctx, String propName) { return getBooleanProperty(ctx, propName, false); } // ============================================================================== // Map member access // ============================================================================== public static String getString(Map<String,Object> map, String propName, String defaultValue) { Object v = map!=null ? map.get(propName) : null; return asString(v,defaultValue); } public static String getString(Map<String,Object> map, String propName) { Object v = map!=null ? map.get(propName) : null; return asString(v); } public static int getInteger(Map<String,Object> map, String propName, int defaultValue) { Object v = map!=null ? map.get(propName) : null; return asInteger(v,defaultValue); } public static int getInteger(Map<String,Object> map, String propName) { Object v = map!=null ? map.get(propName) : null; return asInteger(v); } public static long getLong(Map<String,Object> map, String propName, long defaultValue) { Object v = map!=null ? map.get(propName) : null; return asLong(v,defaultValue); } public static long getLong(Map<String,Object> map, String propName) { Object v = map!=null ? map.get(propName) : null; return asLong(v); } public static double getDouble(Map<String,Object> map, String propName, double defaultValue) { Object v = map!=null ? map.get(propName) : null; return asDouble(v,defaultValue); } public static double getDouble(Map<String,Object> map, String propName) { Object v = map!=null ? map.get(propName) : null; return asDouble(v); } public static boolean getBoolean(Map<String,Object> map, String propName, boolean defaultValue) { Object v = map!=null ? map.get(propName) : null; return asBoolean(v,defaultValue); } public static boolean getBoolean(Map<String,Object> map, String propName) { Object v = map!=null ? map.get(propName) : null; return asBoolean(v); } @SuppressWarnings("unchecked") // $NON-NLS-1$ public static Map<String,Object> getMap(Map<String,Object> map, String propName) { if(map!=null) { Map<String,Object> v = (Map<String,Object>)map.get(propName); return v; } return null; } // ============================================================================== // Style utility // ============================================================================== public static String concatStyleClasses(String s1, String s2) { if(StringUtil.isNotEmpty(s1)) { if(StringUtil.isNotEmpty(s2)) { return s1 + " " + s2; } return s1; } else { if(StringUtil.isNotEmpty(s2)) { return s2; } return ""; } } public static String concatStyles(String s1, String s2) { if(StringUtil.isNotEmpty(s1)) { if(StringUtil.isNotEmpty(s2)) { return s1 + ";" + s2; } return s1; } else { if(StringUtil.isNotEmpty(s2)) { return s2; } return ""; } } public static String concatPath(String path1, String path2, char sep) { if(StringUtil.isEmpty(path1)) { return path2; } if(StringUtil.isEmpty(path2)) { return path1; } StringBuilder b = new StringBuilder(); if(path1.charAt(path1.length()-1)==sep) { b.append(path1,0,path1.length()-1); } else { b.append(path1); } b.append(sep); if(path2.charAt(0)==sep) { b.append(path2,1,path2.length()); } else { b.append(path2); } return b.toString(); //853... //PathUtil.concat(path1,path2,sep); } public static String getPageXspUrl(String pageName) { if(StringUtil.isNotEmpty(pageName)) { if(!pageName.startsWith("/")) { pageName = "/" + pageName; } // if(pageName.startsWith("/")) { // pageName = pageName.substring(1); // } if(!pageName.endsWith(".xsp")) { // $NON-NLS-1$ pageName = pageName + ".xsp"; // $NON-NLS-1$ } return pageName; } return null; } public static String getPageLabel(String pageName) { if(StringUtil.isNotEmpty(pageName)) { int pos = pageName.lastIndexOf('/'); if(pos>=0) { if(pos+1<pageName.length()) { pageName = pageName.substring(pos+1); } else { pageName = ""; } } if(pageName.endsWith(".xsp")) { // $NON-NLS-1$ pageName = pageName.substring(0,pageName.length()-4); } return pageName; } return null; } // ============================================================================== // Calculate the client id from an id // ============================================================================== /** * Calculate the client ID of a component, giving its id. * The the id parameter is already a client ID, then it is returned as is. * @return the clientId, or not if the component does not exist */ public static String getClientId(FacesContext context, UIComponent start, String id, boolean forRefresh) { if(StringUtil.isNotEmpty(id)) { // If it is a client id, then return it if(id.indexOf(NamingContainer.SEPARATOR_CHAR)>=0) { return id; } // Else, find the component and return its client id UIComponent c = FacesUtil.getComponentFor(start, id); if(c!=null) { // In case of partial refresh, we look for a delegated id if(forRefresh) { if(c instanceof FacesNestedDataTable){ return ((FacesNestedDataTable)c).getOuterTableClientId(context); } if(c instanceof FacesRefreshableComponent) { return ((FacesRefreshableComponent)c).getNonChildClientId(context); } } return c.getClientId(context); } } return null; } // ============================================================================== // Ajax utility // ============================================================================== /** * Compose the URL for an Ajax partial refresh request related. */ public static String getPartialRefreshUrl(FacesContext context, UIComponent component) { ExternalContext ctx = context.getExternalContext(); String contextPath = ctx.getRequestContextPath(); String servletPath = ctx.getRequestServletPath(); StringBuilder b = new StringBuilder(); b.append(contextPath); b.append(servletPath); // Add the component id String ajaxId = component.getClientId(context); b.append('?'); b.append(AjaxUtil.AJAX_COMPID); b.append("="); b.append(ajaxId); // Add the view specific id String vid = UniqueViewIdManager.getUniqueViewId(context.getViewRoot()); if(StringUtil.isNotEmpty(vid)) { b.append('&'); b.append(AjaxUtil.AJAX_VIEWID); b.append("="); b.append(vid); } return b.toString(); } // ============================================================================== // Create a valid JavaScript function name from an HTML id // ============================================================================== public static String encodeJSFunctionName(String id) { // TODO this doesn't handle chinese characters in the clientId // which will not be valid in a JavaScript function name, // and should be escaped like the .xsp file names are escaped to .java file names. // Need to handle more characters than just : and - StringBuilder b = new StringBuilder(); int len = id.length(); for(int i=0; i<len; i++) { char c = id.charAt(i); if(c==':' || c=='-') { b.append('_'); } else { b.append(c); } } return b.toString(); } // ============================================================================== // Exception // ============================================================================== public static FacesExceptionEx newException(String msg, Object... params) { String text = StringUtil.format(msg,params); return new FacesExceptionEx(text); } public static FacesExceptionEx newException(Throwable t, String msg, Object... params) { String text = StringUtil.format(msg,params); return new FacesExceptionEx(text,t); } // ============================================================================== // State management // ============================================================================== /** * Save the state of the view if it requires it. */ public static void saveViewState(FacesContext context) { FacesUtil.saveViewState(context); } // ============================================================================== // Reading resource from the library or one of its fragments // ============================================================================== public static URL getResourceURL(Bundle bundle, String path) { int fileNameIndex = path.lastIndexOf('/'); String fileName = path.substring(fileNameIndex+1); path = path.substring(0, fileNameIndex+1); // see http://www.osgi.org/javadoc/r4v42/org/osgi/framework/Bundle.html // #findEntries%28java.lang.String,%20java.lang.String,%20boolean%29 Enumeration<?> urls = bundle.findEntries(path, fileName, false/*recursive*/); if( null != urls && urls.hasMoreElements() ){ URL url = (URL) urls.nextElement(); if( null != url ){ return url; } } return null; // no match, 404 not found. } // ================================================================ // Handling parameters // ================================================================ public static void pushParameters(FacesContext context, Map<String,String> parameters) { // Push the parameters to the request scope if(parameters!=null) { // TODO why? the request params are already in the requestScope. Map<String, Object> req = TypedUtil.getRequestMap(context.getExternalContext()); for (Map.Entry<String, String> e : parameters.entrySet()) { req.put(e.getKey(), e.getValue()); } } } public static String readParameter(FacesContext context, String name) { // PHAN9E4HUH ClassCastException casting to String Object reqMapObj = context.getExternalContext().getRequestMap().get(name); String value = (reqMapObj instanceof String)?(String)reqMapObj : null; if(StringUtil.isEmpty(value)) { value = (String)context.getExternalContext().getRequestParameterMap().get(name); } return value; } // ============================================================================== // Assigning a binding bean to a component // ============================================================================== public static void assignBindingProperty(FacesContext context, String bindingExpr, UIComponent component) { ValueBinding binding = ((ApplicationEx)context.getApplication()).createValueBinding(bindingExpr); if( binding.isReadOnly(context) ){ return; } if( binding instanceof ComponentBindingObject ){ ((ComponentBindingObject)binding).setComponent(component); } binding.setValue(context, component); component.setValueBinding("binding", binding); //$NON-NLS-1$ } /** * Return the UIComponent with the specified id, starting from a particular * component. Same as {@link FacesUtil#getComponentFor(UIComponent, String)}, * except the 8.5.2 FacesUtil method didn't find "for" components that * were within facets. Provided as a workaround for SPR#MKEE86YD5L. * * @designer.publicmethod * @deprecated use {@link FacesUtil#getComponentFor(UIComponent, String)} instead (it has been fixed). */ static public UIComponent getComponentFor(UIComponent start, String id) { return FacesUtil.getComponentFor(start, id); } // ============================================================================== // Render a pending action // ============================================================================== public static void addScript(FacesContext context, String script) { if(StringUtil.isNotEmpty(script)) { UIScriptCollector sc = UIScriptCollector.find(); sc.addScript(script); } } public static void postScript(FacesContext context, String script) { if(StringUtil.isNotEmpty(script)) { UIScriptCollector sc = UIScriptCollector.find(); sc.postScript(script); } } /** * This is left for compatibility, but should be avoided. Prefer * - addScript() when you want to render a script during render time, and when the control * is actually rendered * - postScript() to post a script at anytime (any JSF phase), and regardless of what control * is being rendered. * @deprecated */ public static void addPendingScript(FacesContext context, String script) { // Note that this should change with the new feature in 853 for contributing script! if(AjaxUtil.isAjaxPartialRefresh(context)) { boolean isRendering = AjaxUtil.isRendering(context); AjaxUtil.setRendering(context, true); try { ScriptResource r = new ScriptResource(); r.setClientSide(true); r.setContents(script); ((UIViewRootEx)context.getViewRoot()).addEncodeResource(r); return; } finally { AjaxUtil.setRendering(context, isRendering); } } UIScriptCollector sc = UIScriptCollector.find(); sc.addScript(script); } }