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

com.dukescript.api.javafx.beans.FXBeanInfo Maven / Gradle / Ivy

There is a newer version: 0.6
Show newest version
package com.dukescript.api.javafx.beans;

/*-
 * #%L
 * DukeScript JavaFX Extensions - a library from the "DukeScript" project.
 * %%
 * Copyright (C) 2018 Dukehoff GmbH
 * %%
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * #L%
 */

import com.dukescript.impl.javafx.beans.ConstantValue;
import com.dukescript.spi.javafx.beans.FXBeanInfoProvider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import javafx.beans.property.ReadOnlyProperty;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;

/** Data with information about {@linkplain #getProperties() properties}
 * and {@linkplain #getFunctions() actions} of a JavaFX bean. Beans supporting this
 * protocol should implement {@link Provider} - use its {@link Provider#getFXBeanInfo()}
 * method to obtain instance of the info for given bean.
 * 

* If you have a bean willing to provide the info, use {@link FXBeanInfo#create(java.lang.Object)} * and {@link Builder} to create it. Then return it from your * {@link Provider#getFXBeanInfo()} method. */ public final class FXBeanInfo { private final Object bean; private final Object extra; private final Map> properties; private final Map>> functions; private FXBeanInfo( Object bean, Map> properties, Map>> functions ) { this.bean = bean; this.properties = properties == null ? Collections.emptyMap() : Collections.unmodifiableMap(properties); this.functions = functions == null ? Collections.emptyMap() : Collections.unmodifiableMap(functions); List data = new ArrayList<>(); if (bean!=null){ for (FXBeanInfoProvider p : ServiceLoader.load(FXBeanInfoProvider.class)) { Object info = p.findExtraInfo(this); if (info != null) { data.add(info); } } } this.extra = data.isEmpty() ? null : data.size() == 1 ? data.get(0) : data.toArray(); } /** The associated bean. * * @return object this info {@linkplain #create(java.lang.Object) was created} for */ public Object getBean() { return bean; } /** Properties of {@linkplain #getBean() this bean}. * * @return immutable map of available properties */ public Map> getProperties() { return properties; } /** Invocable handlers for {@linkplain #getBean() this bean}. * * @return immutable map of available event handlers */ public Map>> getFunctions() { return functions; } /** Extra information associated with this bean info. * * @param type of the information * @param type class describing the requested type * @return {code null} or an instance of requested type * @see FXBeanInfoProvider */ public T lookup(Class type) { if (type.isArray()) { return null; } if (type.isInstance(extra)) { return type.cast(extra); } if (extra instanceof Object[]) { for (Object e : ((Object[]) extra)) { if (type.isInstance(e)) { return type.cast(e); } } } return null; } /** Single method interface for JavaFX beans that can provide {@link FXBeanInfo}. * Use {@link #create(java.lang.Object)} and {@link Builder#build()} to * create an instance of your info in your bean constructor and assign * it to a {@code final} field. Then return it from the {@link #getFXBeanInfo()} * method. */ public static interface Provider { /** Provides the info about {@code this} bean. Return always the same * value of the info for the same bean. * * @return constant info describing {@code this} bean */ FXBeanInfo getFXBeanInfo(); } /** * Start creating new {@link FXBeanInfo} for a provided {@code bean}. * Use {@link Builder} methods like {@link Builder#property(javafx.beans.property.ReadOnlyProperty)} * or {@link Builder#action(javafx.beans.property.ReadOnlyProperty)} followed * by final {@link Builder#build()} to create the info. * * @param bean the bean to create the info for * @return builder to create the info with */ public static Builder create(Object bean) { return new FXBeanInfo(null, null, null).new Builder(bean); } /** * Builder (obtained via {@link #create(java.lang.Object)} method) to * create a description of a JavaFX bean and represent it as * {@link FXBeanInfo}. */ public final class Builder { private Object bean; private Map> properties; private Map>> functions; Builder(Object bean) { this.bean = bean; } /** Registers another property into be builder. * * @param p the property * @return {@code this} */ public Builder property(ReadOnlyProperty p) { return property(p.getName(), p); } /** Registers another property/value with provided name into the * builder. * * @param name non-null name of the property * @param p the observable value * @return {@code this} */ public Builder property(String name, ObservableValue p) { Objects.requireNonNull(p, "Property must have a name: " + p); if (this.properties == null) { this.properties = new LinkedHashMap<>(); } this.properties.put(name, p); return this; } /** Registers a property with a constant value. * * @param type of the value * @param name non-null name of the property * @param value the constant value of the property * @return {@code this} */ public Builder constant(String name, T value) { return property(name, new ConstantValue(value)); } public Builder action(ReadOnlyProperty> p) { if (this.functions == null) { this.functions = new LinkedHashMap<>(); } final String name = p.getName(); if (name == null) { throw new NullPointerException("No name for " + p); } ReadOnlyProperty> prev = this.functions.put(name, p); if (prev != null) { this.functions.put(name, prev); throw new IllegalArgumentException("Cannot redefine " + prev + " with " + p); } return this; } /** * Builds {@link FXBeanInfo} from provided properties and actions. * It clears the so far specified values. * @return the JavaFX bean info */ public FXBeanInfo build() { FXBeanInfo info = new FXBeanInfo(bean, this.properties, this.functions); this.properties = null; this.functions = null; return info; } } }