All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.vertigo.ui.impl.springmvc.util.UiUtil Maven / Gradle / Ivy

There is a newer version: 4.2.0
Show newest version
/**
 * vertigo - application development platform
 *
 * Copyright (C) 2013-2021, Vertigo.io, [email protected]
 *
 * 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 io.vertigo.ui.impl.springmvc.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;

import io.vertigo.basics.formatter.FormatterDefault;
import io.vertigo.core.lang.Assertion;
import io.vertigo.core.lang.BasicType;
import io.vertigo.core.lang.WrappedException;
import io.vertigo.core.locale.LocaleManager;
import io.vertigo.core.node.Node;
import io.vertigo.core.util.StringUtil;
import io.vertigo.datamodel.smarttype.SmartTypeManager;
import io.vertigo.datamodel.smarttype.definitions.SmartTypeDefinition;
import io.vertigo.datamodel.structure.definitions.DtDefinition;
import io.vertigo.datamodel.structure.definitions.DtField;
import io.vertigo.datamodel.structure.definitions.DtProperty;
import io.vertigo.datamodel.structure.definitions.Formatter;
import io.vertigo.ui.core.AbstractUiListUnmodifiable;
import io.vertigo.ui.core.ViewContext;
import io.vertigo.vega.webservice.model.UiList;
import io.vertigo.vega.webservice.model.UiObject;

/**
 * Class utilitaire pour le rendu des pages en jsp/ftl.
 * @author npiedeloup
 */
public final class UiUtil implements Serializable {

	private static final long serialVersionUID = -5677843485950859547L;
	private static final Formatter DEFAULT_FORMATTER = new FormatterDefault(null);//by convention : no args

	/**
	 * Constructor.
	 */
	//can't be private, because an instance must be put into struts context, for access from tags.
	public UiUtil() {
		//empty
	}

	/**
	 * @param uiObject Object du context
	 * @return Nom de l'object dans le context
	 */
	public static String contextKey(final UiObject uiObject) {
		final ViewContext viewContext = UiRequestUtil.getCurrentViewContext();
		return viewContext.findKey(uiObject);
	}

	/**
	 * @param object Object in context
	 * @param field field of object
	 * @param row index in list (nullable)
	 * @return Name in context (use for input name)
	 */
	public static String generateComponentUID(final String component, final String object, final String field, final String row) {
		final StringBuilder prefix = new StringBuilder(component)
				.append(Long.toHexString(UUID.randomUUID().getLeastSignificantBits()))
				.append("_");
		return contextGet(prefix.toString(), object, field, row, true);
	}

	/**
	 * @param object Object in context
	 * @param field field of object
	 * @param row index in list (nullable)
	 * @return Name in context (use for input name)
	 */
	public static String contextKey(final String object, final String field, final String row) {
		return contextGet("vContext", object, field, row, true); //use quote because it's evaluated by vueJs
	}

	/**
	 * @param object Object in context
	 * @param field field of object
	 * @param row index in list (nullable)
	 * @return Name in context (use for input name)
	 */
	public static String contextGet(final String object, final String field, final String row) {
		final boolean useQuotes = row != null && !row.matches("[0-9]+"); //row is not number
		return contextGet("model.vContext", object, field, row, useQuotes); //no quotes, when it's evaluated server side
	}

	/**
	 * @param object Object in context
	 * @param field field of object
	 * @param row index in list (nullable)
	 * @return Name in vueData
	 */
	public static String vueDataKey(final String object, final String field, final String row) {
		return "vueData." + object + (row != null ? "[" + row + "]" : "") + (field != null ? "." + field : "");
	}

	private static String contextGet(final String prefix, final String object, final String field, final String row, final boolean useQuotes) {
		final StringBuilder output = new StringBuilder();
		if (useQuotes) {
			output.append('\'');
		}
		output.append(prefix)
				.append('[')
				.append(object)
				.append(']');
		if (row != null) {
			output.append('[');
			if (!useQuotes) {//row is number
				output.append(row);
			} else {//row is expression
				output.append("'+").append(row).append("+'");
			}
			output.append(']');
		}
		if (field != null) {
			output.append('[')
					.append(field)
					.append(']');
		}
		if (useQuotes) {
			output.append('\'');
		}
		return output.toString();
	}

	/**
	 * @param uiList List du context
	 * @param uiObject Objet de la liste
	 * @return index de l'objet dans sa liste
	 */
	public static int indexOf(final List uiList, final UiObject uiObject) {
		return uiList.indexOf(uiObject);
	}

	/**
	 * @param fieldPath Chemin du champ
	 * @return Label du champs
	 */
	public static String label(final String fieldPath) {
		return getDtField(fieldPath).getLabel().getDisplay();
	}

	/**
	 * @param fieldPath Chemin du champ
	 * @return unit of the field
	 */
	public static String smartTypeUnit(final String object, final String fieldName, final String overrideValue) {
		if (overrideValue != null) {
			return overrideValue;
		} else if (fieldName != null) {
			return getDtField(object + '.' + fieldName).getSmartTypeDefinition().getProperties().getValue(DtProperty.UNIT);
		}
		return "";
	}

	/**
	 * @param fieldPath Chemin du champ
	 * @return maxLength of the field
	 */
	public static Integer smartTypeMaxLength(final String object, final String fieldName) {
		if (fieldName != null) {
			return getDtField(object + '.' + fieldName).getSmartTypeDefinition().getProperties().getValue(DtProperty.MAX_LENGTH);
		}
		return null;
	}

	/**
	 * @param fieldPath Chemin du champ
	 * @return css of the field
	 */
	public static String smartTypeCss(final String object, final String fieldName, final String overrideValue, final String defaultValue) {
		if (overrideValue != null) {
			return overrideValue;
		} else if (fieldName != null) {
			return "col_" + getDtField(object + '.' + fieldName).getSmartTypeDefinition().getName();
		}
		return defaultValue;
	}

	/**
	 * @param fieldPath Chemin du champ
	 * @return align direction of the field
	 */
	public static String smartTypeAlign(final String object, final String fieldName, final String overrideValue) {
		if (overrideValue != null) {
			return overrideValue;
		} else if (fieldName != null) {
			final SmartTypeDefinition smartTypeDefinition = getDtField(object + '.' + fieldName).getSmartTypeDefinition();
			if (smartTypeDefinition.getScope().isPrimitive()) {
				final BasicType dataType = smartTypeDefinition.getBasicType();
				switch (dataType) {
					case Long:
					case Integer:
					case Double:
					case BigDecimal:
						return "right";
					case Boolean:
					case Instant:
					case LocalDate:
					case String:
					case DataStream:
					default:
						return "left";
				}
			}
		}
		return "left";
	}

	/**
	 * @param fieldPath Chemin du champ boolean
	 * @param value Valeur à formater
	 * @return rendu du champs boolean
	 */
	public static String formatBoolean(final String fieldPath, final Boolean value) {
		final SmartTypeManager smartTypeManager = Node.getNode().getComponentSpace().resolve(SmartTypeManager.class);
		if (!fieldPath.contains(".")) { //cas des ContextRef sans domain
			return DEFAULT_FORMATTER.valueToString(value, BasicType.Boolean);
		}
		return smartTypeManager.valueToString(getDtField(fieldPath).getSmartTypeDefinition(), value);
	}

	public static Double getMinValue(final String fieldPath) {
		return getDtField(fieldPath).getSmartTypeDefinition().getProperties().getValue(DtProperty.MIN_VALUE);
	}

	public static Double getMaxValue(final String fieldPath) {
		return getDtField(fieldPath).getSmartTypeDefinition().getProperties().getValue(DtProperty.MAX_VALUE);
	}

	public static Double getStep(final Double minValue, final Double maxValue) {
		Assertion.check()
				.isNotNull(minValue)
				.isNotNull(maxValue)
				.isTrue(maxValue > minValue, "Unable to calculate step : maxValue '{0}' must be superior to minValue '{1}'", maxValue, minValue);
		//---
		final double rawStep = (maxValue - minValue) / 200; // we allow at max 200 possible values

		final double index = Math.floor(Math.log10(rawStep));
		final double step = Math.pow(10, index);
		if (rawStep <= step) {
			return step;
		} else if (rawStep <= 2 * step) {
			return 2 * step;
		} else if (rawStep <= 5 * step) {
			return 5 * step;
		} else {
			return 10 * step;
		}

	}

	/**
	 * @param fieldPath Chemin du champ
	 * @return Si le champs est obligatoire
	 */
	public static boolean required(final String fieldPath) {
		Assertion.check().isTrue(fieldPath.indexOf('.') != 0, "FieldPath shouldn't starts with . ({0})", fieldPath);
		//-----
		if (fieldPath.indexOf('.') > 0) { //Le champs est porté par un Object
			return getDtField(fieldPath).getCardinality().hasOne();
		}
		return false; //on ne sait pas dire, mais on ne force pas à obligatoire
	}

	/**
	 * @param uiList liste du context
	 * @return Nom du champ display de cette liste
	 */
	public static String getDisplayField(final String uiListKey) {
		final DtDefinition dtDefinition = getUiList(uiListKey).getDtDefinition();
		return dtDefinition.getDisplayField().get().getName();
	}

	/**
	 * @param uiList liste du context
	 * @return Nom du champ de l'id de cette liste
	 */
	public static String getIdField(final String uiListKey) {
		final UiList uiList = getUiList(uiListKey);
		if (uiList instanceof AbstractUiListUnmodifiable) {
			return ((AbstractUiListUnmodifiable) uiList).getIdFieldName();
		}
		final DtDefinition dtDefinition = getUiList(uiListKey).getDtDefinition();
		return dtDefinition.getIdField().get().getName();
	}

	/**
	 * Get the current locale prefix (either the user's or the node (see LocaleManager javadoc)
	 * ex : fr, en-us, en-gb, es, it
	 * @return the locale (in the quasar's style) to download the right js file
	 */
	public static String getCurrentLocalePrefixForQuasar() {
		final LocaleManager localeManager = Node.getNode().getComponentSpace().resolve(LocaleManager.class);
		final String currentLocaleTag = localeManager.getCurrentLocale().toLanguageTag();
		// not so great but not other solutions (quasar's doesn't respect the standard...)
		if (currentLocaleTag.startsWith("fr")) {
			return "fr";
		} else if (currentLocaleTag.startsWith("es")) {
			return "es";
		} else if (currentLocaleTag.startsWith("it")) {
			return "it";
		} else if ("en".equals(currentLocaleTag)) {
			return "en-us"; //we need to make a choice...
		} else {
			return currentLocaleTag.toLowerCase();
		}
	}

	/**
	 * Get the current locale tag (either the user's or the node (see LocaleManager javadoc)
	 * ex : fr, enUs, enGb, es, it
	 * @return the locale (in the quasar's style) to select the right language in quasar
	 */
	public static String getCurrentLocaleForQuasar() {
		return StringUtil.constToLowerCamelCase(getCurrentLocalePrefixForQuasar().toUpperCase().replace('-', '_'));
	}

	public static String compileVueJsTemplate(final String template) {
		final JsonObject requestParameter = new JsonObject();
		requestParameter.add("template", new JsonPrimitive(template));
		final JsonObject compiledTemplate = callRestWS("http://localhost:8083/", GSON.toJson(requestParameter), JsonObject.class);
		final String render = compiledTemplate.get("render").getAsString();
		final List staticRenderFns = StreamSupport.stream(compiledTemplate.get("staticRenderFns").getAsJsonArray().spliterator(), false)
				.map(JsonElement::getAsString)
				.collect(Collectors.toList());

		final StringBuilder renderJsFunctions = new StringBuilder(",\r\n");
		renderJsFunctions.append("render (h) {\r\n")
				.append(render).append(" \r\n")
				.append("},\r\n")
				.append("  staticRenderFns : [\r\n");
		staticRenderFns.forEach(staticFn -> renderJsFunctions
				.append("		  function () {\r\n")
				.append(staticFn).append(" \r\n")
				.append("		  }\r\n"));
		renderJsFunctions.append("]\r\n");
		return renderJsFunctions.toString();
	}

	private static final Gson GSON = new GsonBuilder().create();

	private static  R callRestWS(final String wsUrl, final String jsonPayload, final Type returnType) {
		Assertion.check().isNotBlank(wsUrl);
		// ---
		try {
			final URL url = new URL(wsUrl);
			final HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
			httpURLConnection.setConnectTimeout(500);
			httpURLConnection.setRequestMethod("POST");
			httpURLConnection.setRequestProperty("Content-Type", "application/json");
			httpURLConnection.setRequestProperty("Accept", "application/json");
			httpURLConnection.setDoOutput(true);

			try (OutputStream os = httpURLConnection.getOutputStream()) {
				final byte[] input = jsonPayload.getBytes(StandardCharsets.UTF_8);
				os.write(input, 0, input.length);
			}

			final ByteArrayOutputStream result = new ByteArrayOutputStream();
			final byte[] buffer = new byte[1024];
			try (InputStream inputStream = httpURLConnection.getInputStream()) {
				int length;
				while ((length = inputStream.read(buffer)) != -1) {
					result.write(buffer, 0, length);
				}
			}
			return GSON.fromJson(result.toString(StandardCharsets.UTF_8), returnType);
		} catch (final IOException e) {
			throw WrappedException.wrap(e);
		}

	}

	private static UiList getUiList(final String uiListKey) {
		final ViewContext viewContext = UiRequestUtil.getCurrentViewContext();
		return (UiList) viewContext.get(uiListKey);
	}

	private static DtField getDtField(final String fieldPath) {
		Assertion.check().isTrue(fieldPath.indexOf('.') > 0, "Le champs n'est pas porté par un Object ({0})", fieldPath);
		//Assertion.check().argument(fieldPath.indexOf('.') == fieldPath.lastIndexOf('.'), "Seul un point est autorisé ({0})", fieldPath);
		final String contextKey = fieldPath.substring(0, fieldPath.lastIndexOf('.'));
		final String fieldName = fieldPath.substring(fieldPath.lastIndexOf('.') + 1);
		final ViewContext viewContext = UiRequestUtil.getCurrentViewContext();
		final Object contextObject = viewContext.get(contextKey);
		Assertion.check()
				.isNotNull(contextObject, "{0} n''est pas dans le context", contextKey)
				.isTrue(contextObject instanceof UiObject || contextObject instanceof UiList, "{0}({1}) doit être un UiObject ou une UiList ", contextKey,
						contextObject.getClass().getSimpleName());

		final DtDefinition dtDefinition;
		if (contextObject instanceof UiObject) {
			dtDefinition = ((UiObject) contextObject).getDtDefinition();
		} else {
			dtDefinition = ((UiList) contextObject).getDtDefinition();
		}
		Assertion.check()
				.isNotNull(dtDefinition) //, "{0}({1}) doit être un UiObject ou un UiList ", contextKey, contextObject.getClass().getSimpleName());
				.isNotNull(dtDefinition, "{0}({1}) doit être un UiObject ou un UiList ", contextKey, contextObject.getClass().getSimpleName());
		return dtDefinition.getField(fieldName);

	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy