
de.beyondjava.angularFaces.components.puiModelSync.PuiModelSync Maven / Gradle / Ivy
/**
* (C) 2013-2014 Stephan Rauh http://www.beyondjava.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package de.beyondjava.angularFaces.components.puiModelSync;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger;
import javax.el.PropertyNotFoundException;
import javax.faces.application.FacesMessage;
import javax.faces.application.ProjectStage;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.FacesComponent;
import javax.faces.component.StateHelper;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.component.ValueHolder;
import javax.faces.component.html.HtmlBody;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import de.beyondjava.angularFaces.components.puiSync.JSONUtilities;
import de.beyondjava.angularFaces.core.ELTools;
import de.beyondjava.angularFaces.core.transformation.AttributeUtilities;
/**
* PuiBody is an HtmlBody that activates the AngularJS/AngularDart framework.
*/
@FacesComponent("de.beyondjava.kendoFaces.puiBody.PuiBody")
public class PuiModelSync extends HtmlBody {
private static final String JSF_ATTRIBUTES_SESSION_PARAMETER = "de.beyondjava.angularFaces.jsfAttributes";
private static final String JSF_ATTRIBUTES_SESSION_CACHE = "de.beyondjava.angularFaces.cache";
private static final Logger LOGGER = Logger.getLogger("de.beyondjava.kendoFaces.puiBody.PuiBody");
/**
* This method is not as superfluous as it seems. We need it to be able to call getStateHelper() in defender methods.
*/
@Override
public StateHelper getStateHelper() {
return super.getStateHelper();
}
public static boolean isJSFAttributesTableEmpty() {
Map sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
Map jsfAttributes = (Map) sessionMap.get(JSF_ATTRIBUTES_SESSION_PARAMETER);
return (null == jsfAttributes || jsfAttributes.isEmpty());
}
public static void initJSFAttributesTable() {
Map sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
if (!sessionMap.containsKey(JSF_ATTRIBUTES_SESSION_PARAMETER)) {
sessionMap.put(JSF_ATTRIBUTES_SESSION_PARAMETER, new HashMap());
sessionMap.put(JSF_ATTRIBUTES_SESSION_CACHE, new HashMap());
} else {
Map jsfAttributes = (Map) sessionMap.get(JSF_ATTRIBUTES_SESSION_PARAMETER);
jsfAttributes.clear();
}
}
public static void addJSFAttrbitute(String key, UIComponent component, boolean cacheable, boolean onlyOnce) {
Map sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
Map jsfAttributes = (Map) sessionMap.get(JSF_ATTRIBUTES_SESSION_PARAMETER);
String suffix = cacheable ? "1" : "0";
suffix += onlyOnce ? "1" : "0";
jsfAttributes.put(key, component.getClientId() + suffix);
}
/**
* Builds basically a JSON structure from the JSF model.
*
* @param model
* the model to be built
* @param key
* variable name
* @param value
* variable value
* @param cacheable
* if true, the value is only sent if it's different from the value of the same attribute in the previous response
*/
public static void addJSFAttrbituteToAngularModel(Map model, String key, Object value, boolean cacheable) {
Map sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
Map cache = (Map) sessionMap.get(JSF_ATTRIBUTES_SESSION_CACHE);
if (cache.containsKey(key)) {
if (cacheable)
if (FacesContext.getCurrentInstance().isPostback()) {
Object previousValue = cache.get(key);
if (null == value && previousValue == null) {
return;
}
if (null != value && value.equals(previousValue)) {
return;
}
}
cache.remove(key);
}
cache.put(key, value);
String[] keys = key.split("\\.");
Map currentMap = model;
for (int i = 0; i < keys.length - 1; i++) {
if (!currentMap.containsKey(keys[i])) {
currentMap.put(keys[i], new HashMap());
}
final Object object = currentMap.get(keys[i]);
if (!(object instanceof Map)) {
// the parent object has already been stored
return;
}
currentMap = (Map) object;
}
currentMap.put(keys[keys.length - 1], value);
}
public static List getFacesModel() {
Map model = new HashMap();
Map sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
Map jsfAttributes = (Map) sessionMap.get(JSF_ATTRIBUTES_SESSION_PARAMETER);
UIViewRoot root = FacesContext.getCurrentInstance().getViewRoot();
// sessionMap.remove(JSF_ATTRIBUTES_SESSION_PARAMETER);
for (Entry entry : jsfAttributes.entrySet()) {
try {
String attribute = entry.getKey();
final int length = entry.getValue().length();
String id = entry.getValue().substring(0, length - 2);
boolean cacheable = entry.getValue().charAt(length - 2) == '1';
boolean onlyOnce = entry.getValue().charAt(length - 1) == '1';
if (onlyOnce && FacesContext.getCurrentInstance().isPostback())
continue;
UIComponent comp = root.findComponent(id);
if (null != comp && comp instanceof EditableValueHolder) {
Object value = ELTools.evalAsObject("#{" + attribute + "}");
Object valueToRender = getValueToRender(FacesContext.getCurrentInstance(), comp);
if (value != null && valueToRender != null && valueToRender instanceof String) {
try {
valueToRender = convertToDatatype((String) valueToRender, value.getClass());
} catch (Exception e) {
}
}
if (null != valueToRender) {
addJSFAttrbituteToAngularModel(model, attribute, valueToRender, cacheable);
} else {
addJSFAttrbituteToAngularModel(model, attribute, value, cacheable);
}
} else {
Object value = ELTools.evalAsObject("#{" + attribute + "}");
// vex.getValue(FacesContext.getCurrentInstance().getELContext())
addJSFAttrbituteToAngularModel(model, attribute, value, cacheable);
}
} catch (PropertyNotFoundException pureAngularAttribute) {
// probably it's an AngularJS attribute that doesn't have a JSF counterpart, so we don't consider this an error
}
}
List beans = new ArrayList();
String messages = "";
List messageList = FacesContext.getCurrentInstance().getMessageList();
for (FacesMessage message : messageList) {
if (!message.isRendered()) {
String severity = message.getSeverity().toString();
String summary = message.getSummary();
String detail = message.getDetail();
messages += ",{\"severity\":\"" + severity + "\", \"summary\":\"" + summary + "\", \"detail\":\"" + detail + "\"}";
message.rendered();
}
}
if (messages.length() > 0) {
String assignment = "\"" + "facesmessages\", '[" + messages.substring(1) + "]'";
beans.add(assignment);
}
for (Entry bean : model.entrySet()) {
String assignment = "\"" + bean.getKey() + "\",'" + JSONUtilities.writeObjectToJSONString(bean.getValue()) + "'";
beans.add(assignment);
}
return beans;
}
private static Object convertToDatatype(String valueToRender, Class targetClass) {
if ("".equals(valueToRender))
return null;
if (null == valueToRender)
return null;
if (valueToRender.getClass() == targetClass)
return valueToRender;
if (targetClass == int.class)
return new Integer((String) valueToRender).intValue();
if (targetClass == long.class)
return new Long((String) valueToRender).longValue();
if (targetClass == short.class)
return new Short((String) valueToRender).shortValue();
if (targetClass == float.class)
return new Float((String) valueToRender).floatValue();
if (targetClass == double.class)
return new Double((String) valueToRender).doubleValue();
if (targetClass == byte.class)
return new Byte((String) valueToRender).byteValue();
if (targetClass == Integer.class)
return new Integer((String) valueToRender);
if (targetClass == Long.class)
return new Long((String) valueToRender);
if (targetClass == Short.class)
return new Short((String) valueToRender);
if (targetClass == Float.class)
return new Float((String) valueToRender);
if (targetClass == Double.class)
return new Double((String) valueToRender);
if (targetClass == Byte.class)
return new Byte((String) valueToRender);
return null;
}
/**
* This method has been copied from the PrimeFaces 5 project (and been adapted after).
*
* Algorithm works as follows; - If it's an input component, submitted value is checked first since it'd be the value to be used in case
* validation errors terminates jsf lifecycle - Finally the value of the component is retrieved from backing bean and if there's a
* converter, converted value is returned
*
* @param context
* FacesContext instance
* @param component
* UIComponent instance whose value will be returned
* @return End text
*/
public static Object getValueToRender(FacesContext context, UIComponent component) {
if (component instanceof ValueHolder) {
if (component instanceof EditableValueHolder) {
EditableValueHolder input = (EditableValueHolder) component;
Object submittedValue = input.getSubmittedValue();
if (submittedValue == null && context.isValidationFailed() && !input.isValid()) {
return null;
} else if (submittedValue != null) {
return submittedValue;
}
}
ValueHolder valueHolder = (ValueHolder) component;
Object value = valueHolder.getValue();
return value;
}
// component it not a value holder
return null;
}
@Override
public void encodeAll(FacesContext arg0) throws IOException {
encodeBegin(arg0);
}
@Override
public void encodeBegin(FacesContext context) throws IOException {
ResponseWriter writer = context.getResponseWriter();
boolean debugMode = context.isProjectStage(ProjectStage.Development);
if (debugMode) {
writer.append("\r\n");
}
writer.startElement("script", this);
if (debugMode) {
writer.append("\r\n");
}
writer.writeText("window.jsfScope=null;", null);
if (debugMode) {
writer.append("\r\n");
}
writer.writeText("function initJSFScope($scope){", null);
if (debugMode) {
writer.append("\r\n ");
}
writer.write("if (window.jsfScope==null) {");
writer.writeText("window.jsfScope=$scope;", null);
if (debugMode) {
writer.append("\r\n ");
}
if (debugMode) {
writer.append("\r\n ");
}
List beansAsJSon = getFacesModel();
for (String bean : beansAsJSon) {
writer.writeText("puiUpdateModel(" + bean + ");", null);
}
if (debugMode) {
writer.append("\r\n");
}
writer.writeText("}", null);
if (debugMode) {
writer.append("\r\n");
}
writer.writeText("}", null);
if (debugMode) {
writer.append("\r\n");
}
writer.endElement("script");
if (debugMode) {
writer.append("\r\n");
}
String main = AttributeUtilities.getAttributeAsString(this, "angularJSFile");
if (main == null) {
main = "main.js";
}
if (!main.endsWith(".js")) {
main = main + ".js";
}
writer.append("");
}
@Override
public void encodeChildren(FacesContext arg0) throws IOException {
}
@Override
public void encodeEnd(FacesContext arg0) throws IOException {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy