Kaynağa Gözat

move query into assira.ui, fix unit tests

Mariusz Czarnowski 2 yıl önce
ebeveyn
işleme
a0d647f26a

+ 63 - 0
assira.ui/src/main/java/net/ranides/assira/ui/query/IComponent.java

@@ -0,0 +1,63 @@
+package net.ranides.assira.ui.query;
+
+import javax.accessibility.AccessibleContext;
+import net.ranides.assira.generic.Ref;
+import net.ranides.assira.reflection.BeanPropertyBinding;
+import net.ranides.assira.reflection.IClass;
+
+import java.awt.*;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Predicate;
+
+public interface IComponent {
+
+    static IComponent of(Component component) {
+        return new RComponent(component);
+    }
+
+    IClass<?> type();
+
+    IComponent root();
+
+    IComponent parent();
+
+    String name();
+
+    AccessibleContext accessible();
+
+    Map<String, Object> properties();
+
+    BeanPropertyBinding<Object> property(String name);
+
+    void property(String name, Object value);
+
+    void set(String name, Object value);
+
+    Object get(String name);
+
+    IComponents children();
+
+    Ref<IComponent> child(int index);
+
+    Ref<IComponent> child(String name);
+
+    IComponents find(String path);
+
+    IComponents find(String[] path);
+
+    IComponents find(Predicate<IComponent> visitor);
+
+    boolean isContainer();
+
+    boolean isType(Class<?> type);
+
+    Component unwrap();
+
+    <T extends Component> T unwrap(Class<T> type);
+
+    void add(Component component);
+
+    void add(IComponent component);
+
+}

+ 63 - 0
assira.ui/src/main/java/net/ranides/assira/ui/query/IComponentSelector.java

@@ -0,0 +1,63 @@
+package net.ranides.assira.ui.query;
+
+import net.ranides.assira.collection.arrays.ArrayUtils;
+
+import java.awt.*;
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+public class IComponentSelector {
+
+    private final IComponents scope;
+
+    private final String[] path;
+
+    IComponentSelector(IComponents scope, String[] path) {
+        this.scope = scope;
+        this.path = path;
+    }
+
+    public static IComponentSelector of(String path) {
+        return of(IComponentUtils.windows(), path);
+    }
+
+    public static IComponentSelector of(IComponent scope, String path) {
+        return of(IComponents.of(scope), path);
+    }
+
+    public static IComponentSelector of(IComponents scope, String path) {
+        return new IComponentSelector(scope, path.split("[ ./]+"));
+    }
+
+    public IComponents scope() {
+        return scope;
+    }
+
+    public String[] path() {
+        return path;
+    }
+
+    public IComponentSelector parent() {
+        return new IComponentSelector(scope, ArrayUtils.head(path, path.length-1));
+    }
+
+    public IComponents stream() {
+        return scope().find(path);
+    }
+
+    public void forEach(Consumer<? super IComponent> consumer) {
+        stream().forEach(consumer);
+    }
+
+    public IComponent first() {
+        return stream().first().orElseThrow(() -> {
+            return new IllegalArgumentException(Arrays.toString(path) + " does not match any component");
+        });
+    }
+
+    public <T extends Component> T first(Class<T> type) {
+        return stream().first(type).orElseThrow(() -> {
+            return new IllegalArgumentException(Arrays.toString(path) + " does not match any " + type);
+        });
+    }
+}

+ 23 - 0
assira.ui/src/main/java/net/ranides/assira/ui/query/IComponentUtils.java

@@ -0,0 +1,23 @@
+package net.ranides.assira.ui.query;
+
+import lombok.experimental.UtilityClass;
+
+import javax.swing.*;
+import java.awt.*;
+
+@UtilityClass
+public class IComponentUtils {
+
+    public static IComponents windows() {
+        return new RComponents(Window.getWindows());
+    }
+
+    public static void update(String path, Object value) {
+        SwingUtilities.invokeLater(() -> {
+            IPropertySelector.of(path).forEach(property -> {
+                property.set(value);
+            });
+        });
+    }
+
+}

+ 132 - 0
assira.ui/src/main/java/net/ranides/assira/ui/query/IComponents.java

@@ -0,0 +1,132 @@
+package net.ranides.assira.ui.query;
+
+import javax.accessibility.AccessibleContext;
+import net.ranides.assira.collection.query.CQuery;
+import net.ranides.assira.generic.Ref;
+import net.ranides.assira.reflection.BeanPropertyBinding;
+
+import java.awt.*;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collector;
+
+public interface IComponents extends Iterable<IComponent> {
+
+    static IComponents of(IComponent value) {
+        return new RComponents(CQuery.from().value(value));
+    }
+
+    static IComponents of(IComponent... values) {
+        return new RComponents(CQuery.from().array(values));
+    }
+
+    static IComponents empty() {
+        return RComponents.EMPTY.$empty();
+    }
+
+    /**
+     * Returns number of elements.
+     *
+     * Invocation of this method can trigger computation of whole content of collection.
+     *
+     * @return int
+     */
+    int count();
+
+    /**
+     * Returns true if collection is empty.
+     *
+     * Invocation of this method can trigger computation of whole content of collection.
+     *
+     * @return boolean
+     */
+    boolean isEmpty();
+
+    /**
+     * Converts this collection into CQuery.
+     * Each element is converted using provided mapper.
+     *
+     * @param mapper mapper
+     * @param <R> R
+     * @return CQuery
+     */
+    <R> CQuery<R> stream(Function<? super IComponent, R> mapper);
+
+    /**
+     * Converts this collection into CQuery.
+     *
+     * @return CQuery
+     */
+    CQuery<IComponent> stream();
+
+    /**
+     * Iterates over all elements inside collection.
+     *
+     * @param consumer consumer
+     */
+    void each(Consumer<? super IComponent> consumer);
+
+    /**
+     * Converts this collection into List.
+     * Each element is converted using provided mapper.
+     *
+     * @param mapper mapper
+     * @param <R> R
+     * @return List
+     */
+    <R> List<R> list(Function<? super IComponent, R> mapper);
+
+    /**
+     * Converts this collection into List.
+     *
+     * @return List
+     */
+    List<IComponent> list();
+
+    /**
+     * Converts this collection into Collection.
+     *
+     * @param collector collector
+     * @param <R> R
+     * @param <A> A
+     * @return Collection
+     */
+    <R, A> R collect(Collector<? super IComponent, A, R> collector);
+
+    IComponents filter(Predicate<IComponent> predicate);
+
+    <T extends Component> IComponents filter(Class<T> type);
+
+    Ref<IComponent> first();
+
+    <T extends Component> Ref<T> first(Class<T> type);
+
+    Ref<IComponent> single();
+
+    <T extends Component> Ref<T> single(Class<T> type);
+
+    Ref<IComponent> at(int index);
+
+    CQuery<String> names();
+
+    CQuery<AccessibleContext> accessibles();
+
+    IComponents parent();
+
+    IComponents find(String[] path);
+
+    IComponents find(String path);
+
+    IComponents find(Predicate<IComponent> visitor);
+
+    CQuery<BeanPropertyBinding<Object>> property(String name);
+
+    void property(String name, Object value);
+
+    void set(String name, Object value);
+
+    CQuery<Object> get(String name);
+
+}

+ 97 - 0
assira.ui/src/main/java/net/ranides/assira/ui/query/IPropertySelector.java

@@ -0,0 +1,97 @@
+package net.ranides.assira.ui.query;
+
+import net.ranides.assira.collection.arrays.ArrayUtils;
+import net.ranides.assira.collection.query.CQuery;
+import net.ranides.assira.reflection.BeanPropertyBinding;
+
+import java.util.function.Consumer;
+
+public class IPropertySelector {
+
+    private final IComponents scope;
+
+    private final String[] component;
+
+    private final String property;
+
+    private final String qualifier;
+
+    private IPropertySelector(IComponents scope, String[] component, String property, String qualifier) {
+        this.scope = scope;
+        this.component = component;
+        this.property = property;
+        this.qualifier = qualifier;
+    }
+
+    public static IPropertySelector of(String path) {
+        return of(IComponentUtils.windows(), path);
+    }
+
+    public static IPropertySelector of(IComponent scope, String path) {
+        return of(IComponents.of(scope), path);
+    }
+
+    public static IPropertySelector of(IComponents scope, String path) {
+        String[] parts = path.trim().split("[ ./]+");
+        if(parts.length > 2) {
+            if(parts[parts.length-1].startsWith("@")) {
+                String prop = parts[parts.length - 2];
+                String qual = parts[parts.length - 1].substring(1).trim();
+                return new IPropertySelector(scope, ArrayUtils.slice(parts, 0, parts.length-2), prop, qual);
+            }
+        }
+        return new IPropertySelector(scope, ArrayUtils.slice(parts, 0, parts.length-1), parts[parts.length-1], "");
+    }
+
+    public static IPropertySelector of(String path, String property) {
+        return of(IComponentUtils.windows(), path, property);
+    }
+
+    public static IPropertySelector of(IComponent scope, String path, String property) {
+        return of(IComponents.of(scope), path, property);
+    }
+
+    public static IPropertySelector of(IComponents scope, String path, String property) {
+        return of(scope, path + " " + property);
+    }
+
+    public static IPropertySelector parse(String path) {
+        return of(IComponents.empty(), path);
+    }
+
+    public IComponents scope() {
+        return scope;
+    }
+
+    public String[] path() {
+        return component;
+    }
+
+    public String property() {
+        return property;
+    }
+
+    public String qualifier() {
+        return qualifier;
+    }
+
+    public IComponentSelector parent() {
+        return new IComponentSelector(scope, component);
+    }
+
+    public CQuery<BeanPropertyBinding<Object>> stream() {
+        return scope().find(component).stream().map(component -> component.property(property));
+    }
+
+    public CQuery<BeanPropertyBinding<Object>> stream(IComponent scope) {
+        return scope.find(component).stream().map(component -> component.property(property));
+    }
+
+    public CQuery<BeanPropertyBinding<Object>> stream(IComponents scope) {
+        return scope.find(component).stream().map(component -> component.property(property));
+    }
+
+    public void forEach(Consumer<? super BeanPropertyBinding<Object>> consumer) {
+        stream().forEach(consumer);
+    }
+}

+ 189 - 0
assira.ui/src/main/java/net/ranides/assira/ui/query/RComponent.java

@@ -0,0 +1,189 @@
+package net.ranides.assira.ui.query;
+
+import javax.accessibility.AccessibleContext;
+import net.ranides.assira.generic.Ref;
+import net.ranides.assira.reflection.*;
+
+import java.awt.*;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Predicate;
+
+class RComponent implements IComponent {
+
+    private final Component component;
+
+    private final BeanModel model;
+
+    private final BeanModel.FluentMap fluent;
+
+    RComponent(Component component) {
+        this.component = component;
+        this.model = BeanModel.typefor(component);
+        this.fluent = model.fluent(component);
+    }
+
+    @Override
+    public IClass<?> type() {
+        return model.type();
+    }
+
+    @Override
+    public IComponent parent() {
+        return new RComponent(component.getParent());
+    }
+
+    @Override
+    public IComponent root() {
+        Component out = component;
+        while(out.getParent() != null) {
+            out = out.getParent();
+        }
+        return new RComponent(out);
+    }
+
+    @Override
+    public String name() {
+        return component.getName();
+    }
+
+    @Override
+    public AccessibleContext accessible() {
+        return component.getAccessibleContext();
+    }
+
+    @Override
+    public Map<String, Object> properties() {
+        return fluent;
+    }
+
+    @Override
+    public BeanPropertyBinding<Object> property(String name) {
+        BeanPropertyBinding<Object> binding = fluent.getBinding(name);
+        if(binding == null) {
+            throw new IllegalArgumentException("Unknown property " + component + ":" + name);
+        }
+        return binding;
+    }
+
+    @Override
+    public void property(String name, Object value) {
+        fluent.put(name, value);
+    }
+
+    @Override
+    public void set(String name, Object value) {
+        property(name).set(value);
+    }
+
+    @Override
+    public Object get(String name) {
+        return property(name).get();
+    }
+
+    @Override
+    public IComponents children() {
+        if(component instanceof Container) {
+            synchronized (component.getTreeLock()) {
+                return new RComponents(((Container) component).getComponents());
+            }
+        }
+        return RComponents.EMPTY;
+    }
+
+    @Override
+    public Ref<IComponent> child(int index) {
+        if(component instanceof Container) {
+            Container container = (Container) this.component;
+            synchronized (component.getTreeLock()) {
+                if (index < 0 || index >= container.getComponentCount()) {
+                    return Ref.of(new RComponent(container.getComponent(index)));
+                }
+            }
+        }
+        return Ref.empty();
+    }
+
+    @Override
+    public Ref<IComponent> child(String name) {
+        if(component instanceof Container) {
+            Container container = (Container) this.component;
+            synchronized (component.getTreeLock()) {
+                for(Component c : container.getComponents()) {
+                    if(name.equals(c.getName())) {
+                        return Ref.of(new RComponent(c));
+                    }
+                }
+            }
+        }
+        return Ref.empty();
+    }
+
+    @Override
+    public IComponents find(String[] path) {
+        IComponents that = IComponents.of(this);
+
+        for (String name : path) {
+            that = that.find(c -> name.equals(c.name()));
+        }
+
+        return that;
+    }
+
+    @Override
+    public IComponents find(String path) {
+        String[] component = IComponentSelector.of(this, path).path();
+        return find(component);
+    }
+
+    @Override
+    public IComponents find(Predicate<IComponent> visitor) {
+        if(visitor.test(this)) {
+            return IComponents.of(this);
+        }
+
+        if(!isContainer()) {
+            return RComponents.EMPTY;
+        }
+
+        return new RComponents(((Container)component).getComponents()).find(visitor);
+    }
+
+    @Override
+    public boolean isContainer() {
+        return component instanceof Container;
+    }
+
+    @Override
+    public boolean isType(Class<?> type) {
+        return type.isInstance(component);
+    }
+
+    @Override
+    public Component unwrap() {
+        return component;
+    }
+
+    @Override
+    public <T extends Component> T unwrap(Class<T> type) {
+        return type.cast(component);
+    }
+
+    @Override
+    public void add(Component component) {
+        if(!isContainer()) {
+            throw new IllegalArgumentException(this + " is not container");
+        }
+        ((Container)this.component).add(component);
+    }
+
+    @Override
+    public void add(IComponent component) {
+        add(component.unwrap());
+    }
+
+    @Override
+    public String toString() {
+        return "{" + component.getClass() + ":" + component.getName() + "}";
+    }
+}

+ 165 - 0
assira.ui/src/main/java/net/ranides/assira/ui/query/RComponents.java

@@ -0,0 +1,165 @@
+package net.ranides.assira.ui.query;
+
+import javax.accessibility.AccessibleContext;
+import net.ranides.assira.collection.query.CQuery;
+import net.ranides.assira.generic.Ref;
+import net.ranides.assira.reflection.BeanPropertyBinding;
+
+import java.awt.*;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collector;
+
+class RComponents implements IComponents {
+
+    public static final RComponents EMPTY = new RComponents(CQuery.empty());
+
+    private final CQuery<IComponent> query;
+
+    RComponents(Component[] components) {
+        this.query = CQuery.from().array(components).map(IComponent::of);
+    }
+
+    RComponents(CQuery<IComponent> query) {
+        this.query = query;
+    }
+
+    RComponents $empty() {
+        return EMPTY;
+    }
+
+    @Override
+    public IComponents find(String[] path) {
+        return new RComponents(query.flat(c -> c.find(path).stream()));
+    }
+
+    @Override
+    public IComponents find(String path) {
+        return new RComponents(query.flat(c -> c.find(path).stream()));
+    }
+
+    @Override
+    public IComponents find(Predicate<IComponent> visitor) {
+        return new RComponents(query.flat(c -> c.find(visitor).stream()));
+    }
+
+    @Override
+    public CQuery<BeanPropertyBinding<Object>> property(String name) {
+        return stream().map(c -> c.property(name));
+    }
+
+    @Override
+    public void property(String name, Object value) {
+        stream().forEach(c -> c.property(name, value));
+    }
+
+    @Override
+    public void set(String name, Object value) {
+        stream().forEach(c -> c.set(name, value));
+    }
+
+    @Override
+    public CQuery<Object> get(String name) {
+        return stream().map(c -> c.get(name));
+    }
+
+    @Override
+    public int count() {
+        return query.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return query.isEmpty();
+    }
+
+    @Override
+    public <R> CQuery<R> stream(Function<? super IComponent, R> mapper) {
+        return stream().map(mapper);
+    }
+
+    @Override
+    public CQuery<IComponent> stream() {
+        return query;
+    }
+
+    @Override
+    public void each(Consumer<? super IComponent> consumer) {
+        stream().forEach(consumer);
+    }
+
+    @Override
+    public <R> List<R> list(Function<? super IComponent, R> mapper) {
+        return stream(mapper).list();
+    }
+
+    @Override
+    public List<IComponent> list() {
+        return stream().list();
+    }
+
+    @Override
+    public <R, A> R collect(Collector<? super IComponent, A, R> collector) {
+        return stream().collect(collector);
+    }
+
+    @Override
+    public IComponents filter(Predicate<IComponent> predicate) {
+        return new RComponents(query.filter(predicate));
+    }
+
+    @Override
+    public <T extends Component> IComponents filter(Class<T> type) {
+        return new RComponents(query.filter(c -> c.isType(type)));
+    }
+
+    @Override
+    public Ref<IComponent> first() {
+        return stream().first();
+    }
+
+    @Override
+    public <T extends Component> Ref<T> first(Class<T> type) {
+        return query.map(IComponent::unwrap).filter(type).first();
+    }
+
+    @Override
+    public <T extends Component> Ref<T> single(Class<T> type) {
+        return query.map(IComponent::unwrap).filter(type).single();
+    }
+
+    @Override
+    public Ref<IComponent> single() {
+        return stream().single();
+    }
+
+    @Override
+    public Ref<IComponent> at(int index) {
+        return Ref.empty();
+    }
+
+    @Override
+    public CQuery<String> names() {
+        return query.map(IComponent::name);
+    }
+
+    @Override
+    public CQuery<AccessibleContext> accessibles() {
+        return query.map(IComponent::accessible);
+    }
+
+    @Override
+    public IComponents parent() {
+        return new RComponents(query.map(IComponent::parent));
+    }
+
+    @Override
+    public Iterator<IComponent> iterator() {
+        return query.iterator();
+    }
+
+}

+ 4 - 1
assira.ui/src/test/resources/grammars/CssCoverage.txt

@@ -55,6 +55,8 @@
 1: SWING_BORDER_bevel
 1: SWING_BORDER_bevel_soft
 1: SWING_BORDER_etched
+0: SWING_BORDER_lowered
+0: SWING_BORDER_raised
 1: WS
 0: border
 1: borderCSS
@@ -64,8 +66,9 @@
    1: borderSize#borderSize2
    1: borderSize#borderSize4
 0: borderSizeComponent
-0: borderSwingSimple
+1: borderSwingSimple
 0: borderSwingSimple_Style
+0: borderSwingSimple_Type
 1: codeHex
    1: codeHex#codeHex3 <= CODE_HEX3
    1: codeHex#codeHex4 <= CODE_HEX4