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.nuiton.jaxx.runtime.JAXXUtil Maven / Gradle / Ivy
/*
* #%L
* JAXX :: Runtime
* %%
* Copyright (C) 2008 - 2023 Code Lutin, Ultreia.io
* %%
* This program 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 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 Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
package org.nuiton.jaxx.runtime;
import org.apache.commons.beanutils.ConstructorUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nuiton.jaxx.runtime.context.JAXXContextEntryDef;
import org.nuiton.jaxx.runtime.context.JAXXInitialContext;
import java.awt.Component;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeListenerProxy;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EventListener;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
public class JAXXUtil {
static private final Logger log = LogManager.getLogger(JAXXUtil.class);
public static final String PARENT = "parent";
// Maps root objects to lists of event listeners
private static final Map>>
eventListeners = new WeakHashMap<>();
private static final Map>>
dataBindingUpdateListeners = new WeakHashMap<>();
private static final PropertyChangeListener[] EMPTY_ARRAY_PROPERTY_CHANGE_LISTENERS = new PropertyChangeListener[0];
private static class EventListenerDescriptor {
Class> listenerClass;
String listenerMethodName;
String methodName;
Object eventListener;
}
/**
* Decodes the serialized representation of a JAXXObjectDescriptor. The
* string must be a byte-to-character mapping of the binary serialization
* data for a JAXXObjectDescriptor. See the comments in
* JAXXCompiler.createJAXXObjectDescriptorField for the rationale behind
* this (admittedly ugly) approach.
*
* @param descriptor descriptor to decode
* @return the dedoced descriptor
*/
public static JAXXObjectDescriptor decodeJAXXObjectDescriptor(
String descriptor) {
try {
return (JAXXObjectDescriptor) Base64Coder.deserialize(descriptor,
false);
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("Internal error: can't-happen error", e);
}
}
public static JAXXObjectDescriptor decodeCompressedJAXXObjectDescriptor(
String descriptor) {
try {
return (JAXXObjectDescriptor) Base64Coder.deserialize(descriptor,
true);
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("Internal error: can't-happen error", e);
}
}
public static JAXXContextEntryDef newContextEntryDef(
Class klass) {
return newContextEntryDef(null, klass);
}
public static JAXXContextEntryDef newContextEntryDef(
String name, Class> klass) {
return new JAXXContextEntryDef<>(name, (Class) klass);
}
public static JAXXContextEntryDef> newListContextEntryDef() {
return newListContextEntryDef(null);
}
public static JAXXContextEntryDef> newListContextEntryDef(
String name) {
Class> castList = castList();
return new JAXXContextEntryDef<>(name, castList);
}
public static JAXXContextEntryDef> newMapContextEntryDef(
String name) {
return new JAXXContextEntryDef<>(Map.class, name);
}
@SuppressWarnings({"unchecked"})
protected static Class> castMap() {
return (Class>) Collections.emptyMap().getClass();
}
@SuppressWarnings({"unchecked"})
protected static Class> castList() {
return (Class>) List.of().getClass();
}
/**
* Method to initialize the context of a ui.
*
* @param ui the ui
* @param parentContext the context to set in ui
*/
public static void initContext(JAXXObject ui, JAXXContext parentContext) {
if (parentContext instanceof JAXXInitialContext) {
((JAXXInitialContext) parentContext).to(ui);
} else {
ui.setContextValue(parentContext);
}
// if parentContext is a JAXXObject then
// add an special parent entry to can go up
if (parentContext instanceof JAXXObject) {
ui.setContextValue(parentContext, PARENT);
}
}
public static E getEventListener(
Class listenerClass,
final String listenerMethodName,
final Object methodContainer,
String methodName) {
WeakReference> ref =
eventListeners.get(methodContainer);
List descriptors = ref != null ?
ref.get() : null;
if (descriptors == null) {
descriptors = new ArrayList<>();
eventListeners.put(
methodContainer,
new WeakReference<>(
descriptors));
} else {
for (EventListenerDescriptor descriptor : descriptors) {
if (descriptor.listenerClass.equals(listenerClass) &&
(listenerMethodName == null ?
descriptor.listenerMethodName == null :
listenerMethodName.equals(
descriptor.listenerMethodName)) &&
methodName.equals(descriptor.methodName)) {
return (E) descriptor.eventListener;
}
}
}
// else we need to create a new listener
final EventListenerDescriptor descriptor = new EventListenerDescriptor();
descriptor.listenerClass = listenerClass;
descriptor.listenerMethodName = listenerMethodName;
descriptor.methodName = methodName;
try {
final List listenerMethods =
Arrays.asList(listenerClass.getMethods());
Method listenerMethod = null;
if (listenerMethodName != null) {
for (Method listenerMethod1 : listenerMethods) {
if (listenerMethod1.getName().equals(listenerMethodName)) {
listenerMethod = listenerMethod1;
break;
}
}
}
if (listenerMethodName != null && listenerMethod == null) {
throw new IllegalArgumentException(
"no method named " + listenerMethodName +
" found in class " + listenerClass.getName());
}
// tchemit 2010-12-01 : we must the exact method found, some none javaBeans
// api does use different signature for some of them listener
// an exemple is the TableColumnModelListener : http://download.oracle.com/javase/6/docs/api/javax/swing/event/TableColumnModelListener.html
// This fix the bug https://forge.nuiton.org/issues/show/1124
Class>[] parameterTypes;
if (listenerMethodName != null) {
// search an exact method, so must use the exact found listener method
parameterTypes = listenerMethod.getParameterTypes();
} else {
// keep this horrible code which is not safe at all :
// see previous comment
parameterTypes = listenerMethods.get(0).getParameterTypes();
}
Class> methodContainerClass = methodContainer.getClass();
final Method targetMethod =
methodContainerClass.getMethod(methodName, parameterTypes);
descriptor.eventListener =
Proxy.newProxyInstance(listenerClass.getClassLoader(),
new Class>[]{listenerClass},
(proxy, method, args) -> {
String methodName1 = method.getName();
if (listenerMethodName == null &&
listenerMethods.contains(method) ||
methodName1.equals(listenerMethodName)) {
try {
targetMethod.setAccessible(true);
return targetMethod.invoke(
methodContainer, args);
} catch (IllegalAccessException e) {
throw new RuntimeException(
"could not invoke on container " +
methodContainer, e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
if (methodName1.equals("toString")) {
return descriptor.toString();
}
if (methodName1.equals("equals")) {
return descriptor.eventListener == args[0];
}
if (methodName1.equals("hashCode")) {
return descriptor.hashCode();
}
return null;
});
descriptors.add(descriptor);
return (E) descriptor.eventListener;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
public static E getEventListener(
Class listenerClass,
Object methodContainer,
String methodName) {
return getEventListener(listenerClass, null, methodContainer,
methodName);
}
public static DataBindingUpdateListener getDataBindingUpdateListener(
JAXXObject object, String bindingName) {
WeakReference> ref =
dataBindingUpdateListeners.get(object);
List listeners = ref == null ? null :
ref.get();
if (listeners == null) {
listeners = new ArrayList<>();
dataBindingUpdateListeners.put(
object,
new WeakReference<>(
listeners));
} else {
for (DataBindingUpdateListener listener : listeners) {
if (bindingName.equals(listener.getBindingName())) {
return listener;
}
}
}
DataBindingUpdateListener listener =
new DataBindingUpdateListener(object, bindingName);
listeners.add(listener);
return listener;
}
/**
* Remove all listeners registred in given {@code pcs}.
*
* @param pcs the pcs to clean
*/
public static void destroy(PropertyChangeSupport pcs) {
PropertyChangeListener[] listeners = pcs.getPropertyChangeListeners();
for (PropertyChangeListener l : listeners) {
if (log.isInfoEnabled()) {
if (l instanceof PropertyChangeListenerProxy) {
PropertyChangeListenerProxy ll = (PropertyChangeListenerProxy) l;
log.info("remove property change listener " + ll.getPropertyName() + " : " + l);
} else {
log.info("remove property change listener " + l);
}
}
pcs.removePropertyChangeListener(l);
}
}
/**
* Remove all listeners registred in given {@code component}.
*
* @param component the pcs to clean
*/
public static void destroy(Component component) {
PropertyChangeListener[] listeners = component.getPropertyChangeListeners();
for (PropertyChangeListener l : listeners) {
if (log.isInfoEnabled()) {
if (l instanceof PropertyChangeListenerProxy) {
PropertyChangeListenerProxy ll = (PropertyChangeListenerProxy) l;
log.info("remove property change listener " + ll.getPropertyName() + " : " + l);
} else {
log.info("remove property change listener " + l);
}
}
component.removePropertyChangeListener(l);
}
}
/**
* Compute the string representation of an object.
*
* Return empty string if given object is null
*
* @param value the value to write
* @return the string representation of the given object or an empty string
* if object is null.
*/
public static String getStringValue(Object value) {
String result;
result = value == null ? "" : value.toString();
return result;
}
/**
* Test if some entries exists in a given context and throw an
* IllegalArgumentException if not.
*
* @param context the context to test
* @param defs the definitions of entries to seek in context
* @throws IllegalArgumentException if the entry is not found in context.
*/
public static void checkJAXXContextEntries(JAXXContext context,
JAXXContextEntryDef>... defs)
throws IllegalArgumentException {
for (JAXXContextEntryDef> def : defs) {
Object value = def.getContextValue(context);
if (value == null) {
throw new IllegalArgumentException(
"the context entry [" + def + "] ] was not found in " +
"context " + context);
}
}
}
/**
* Test if a type of entry exists in a given context and throw an
* IllegalArgumentException if not found.
*
* If entry is found, return his value in context.
*
* @param the type of required data
* @param context the context to test
* @param def the definition of the entry to seek in context
* @return the value from the context
* @throws IllegalArgumentException if the entry is not found in context.
*/
public static T checkJAXXContextEntry(JAXXContext context,
JAXXContextEntryDef def)
throws IllegalArgumentException {
T value = def.getContextValue(context);
if (value == null) {
throw new IllegalArgumentException(
"the context entry [" + def + "] ] was not found in " +
"context " + context);
}
return value;
}
/**
* To reload a binding, the method will invoke
*
* {@link JAXXBinding#removeDataBinding()}
* {@link JAXXBinding#applyDataBinding()}
*
*
* @param binding the binding to reload.
* @since 2.4.2
*/
public static void reloadBinding(JAXXBinding binding) {
binding.removeDataBinding();
binding.applyDataBinding();
}
/**
* Convinient method to reload a given binding by his id on a JAXX ui.
*
* @param src the ui to treate
* @param bindingId the id of binding to reload.
* @since 2.4.2
*/
public static void reloadBinding(JAXXObject src, String bindingId) {
JAXXBinding dataBinding = src.getDataBinding(bindingId);
if (dataBinding != null) {
reloadBinding(dataBinding);
} else {
if (log.isWarnEnabled()) {
log.warn("Could not found binding[" + bindingId + "] on ui " + src);
}
}
}
/**
* Convinient method to apply more than one binding on a JAXX ui.
*
* @param src the ui to treate
* @param bindings the list of binding to process.
*/
public static void applyDataBinding(JAXXObject src, String... bindings) {
for (String binding : bindings) {
src.applyDataBinding(binding);
}
}
/**
* Convinient method to apply more than one binding on a JAXX ui.
*
* @param src the ui to treate
* @param bindings the list of binding to process.
*/
public static void applyDataBinding(JAXXObject src,
Collection bindings) {
for (String binding : bindings) {
src.applyDataBinding(binding);
}
}
/**
* Convinient method to process more than one binding on a JAXX ui.
*
* @param src the ui to treate
* @param bindings the list of binding to process.
*/
public static void processDataBinding(JAXXObject src, String... bindings) {
for (String binding : bindings) {
src.processDataBinding(binding);
}
}
/**
* Convinient method to remove more than one binding on a JAXX ui.
*
* @param src the ui to treate
* @param bindings the list of binding to process.
*/
public static void removeDataBinding(JAXXObject src, String... bindings) {
for (String binding : bindings) {
src.removeDataBinding(binding);
}
}
/**
* Convinient method to remove all than one binding on a JAXX ui.
*
* @param src the ui to treate
* @since 2.10
*/
public static void removeAllDataBindings(JAXXObject src) {
JAXXBinding[] bindings = src.getDataBindings();
for (JAXXBinding binding : bindings) {
removeDataBinding(src, binding.getId());
}
}
/**
* detects all PropertychangedListener added by Jaxx uis (should be a {@link
* DataBindingListener}
*
* @param propertyNames the array of property names to find
* @param listeners the array of listeners to filter
* @return the filtered listeners
*/
public static PropertyChangeListener[] findJaxxPropertyChangeListener(
String[] propertyNames, PropertyChangeListener... listeners) {
if (listeners == null || listeners.length == 0) {
return EMPTY_ARRAY_PROPERTY_CHANGE_LISTENERS;
}
List pNames = Arrays.asList(propertyNames);
List toRemove =
new ArrayList<>();
for (PropertyChangeListener listener : listeners) {
String pName = null;
PropertyChangeListenerProxy plistener = null;
if (listener instanceof PropertyChangeListenerProxy) {
plistener = (PropertyChangeListenerProxy) listener;
if (!pNames.contains(plistener.getPropertyName())) {
// not on the good property
continue;
}
listener = plistener.getListener();
pName = plistener.getPropertyName();
}
if (plistener != null &&
pName != null &&
listener instanceof DataBindingListener) {
if (log.isDebugEnabled()) {
log.debug("find config listener to remove [" + pName +
"] : " + listener);
}
toRemove.add(plistener);
//toRemove.add(listener);
}
}
return toRemove.toArray(new PropertyChangeListener[toRemove.size()]);
}
/**
* Overrides the commons method to have generict cast fiex.
*
* @param type the type of objet to instanciate
* @param prototype prototype of the constructor
* @param parms params to pass to constructor
* @param type of object to create
* @return the new object
* @throws Exception if something wrong
* @since 2.1
*/
@SuppressWarnings({"unchecked"})
public static O invokeConstructor(Class type,
Class>[] prototype,
Object... parms) throws Exception {
return ConstructorUtils.invokeConstructor(type,
parms,
prototype
);
}
/**
* Copy to clipBoard the content of the given text.
*
* @param text text to copy to clipboard
* @since 2.17
*/
public static void copyToClipBoard(String text) {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
if (log.isInfoEnabled()) {
log.info("Put in clipboard :\n" + text);
}
StringSelection selection = new StringSelection(text);
clipboard.setContents(selection, selection);
}
}