jface databinding:List,Set,Map对象的Observable代理封装

需求描述

对于一个已经存在的集合/映射对象(普通的List,Set,Map,非observable),我们希望把将它转换成一个observable对象,这个observable对象就像是原对象的代理一样,当对observable对象操作(增加删除元素)时,实际是对原对象的操作。 jface为List,Set,Map三种类型提供了对应的三种可写对象WritableList,WritableSet,WritableMap,研究了这三个类的代码,发现它们99%是满足这个需求,然并卵,因为构造函数上设计区别,造成这三个类的构造函数生成的observable对象与原对象是隔离的。 以WritableSet的构造函数为例,WritableSet的构造函数重新用外部传入的Collection的内容构造了一个新的HashSet对象,所以WritableSet中的Set对象与传入的原对象(Collection)没有半毛钱关系:

    public WritableSet(Realm realm, Collection<? extends E> c, Object elementType) {
        // 创建了一个新的Set对象传给父类的构造函数
        super(realm, new HashSet<E>(c), elementType);
        this.elementType = elementType;
    }

只有WritableList类可以实现的上述的需求,而且貌似还是为了兼容之前版本的设计失误,而留的。 参见下面WritableList的构造函数的说明org.eclipse.core.databinding.observable.list.WritableList.WritableList(Realm realm, List<E> toWrap, Object elementType)

    /**
     * Creates a writable list containing elements of the given type, wrapping
     * an existing client-supplied list. Note that for backwards compatibility
     * reasons, the contents of the created WritableList will change with the
     * contents of the given list. If this is not desired,
     * {@link #WritableList(Realm, Collection, Object)} should be used by
     * casting the second argument to {@link Collection}.
     *
     * @param realm
     *            the observable's realm
     * @param toWrap
     *            The java.util.List to wrap
     * @param elementType
     *            can be <code>null</code>
     */
    public WritableList(Realm realm, List<E> toWrap, Object elementType) {
        // 没有对toWrap做任何处理就传给了父类的构造函数,这才是我想要的
        super(realm, toWrap, elementType);
    }

所以这虽然是一个很普遍的需求,但jface并没有提供完整的支持,需要自己写代码支持。好在jface的基础框架比较扎实,所以如果搞清楚jface的类继承结构,自己写代码也不复杂,所以我为List,Set,Map三种类型的分别写了三个类用于实现Observable封装(WrappedObservableList,WrappedObservableSet,WrappedObservableMap),这三个类的代码都参考自对应的WritableList,WritableSet,WritableMap三个类,大部分代码都是直接抄来的。以下是三个类对应的实现代码

WrappedObservableList

WrappedObservableList类实现最简单,因为WritableList本身就因为历史原因支持这种代理封装,所以WrappedObservableList类只是在构造函数中加了参数检查 WrappedObservableList.java

package net.gdface.ui.binding;

import java.util.List;
import java.util.Objects;

import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.list.WritableList;

/**
 * 将指定的{@link List}对象(wrappedList)封装为 {@link ObservableList},<br>
 * 对observable对象的操作同步到原对象<br>
 * @author guyadong
 *
 * @param <E>
 */
public class WrappedObservableList<E> extends WritableList<E> {

    public WrappedObservableList(List<E> wrappedList, Object elementType) {
        this(Realm.getDefault(),wrappedList, elementType);
    }

    public WrappedObservableList(Realm realm, List<E> wrappedList, Object elementType) {
        super(realm, Objects.requireNonNull(wrappedList,"the argument wrappedList must not be null"), elementType);
    }   
}

WrappedObservableSet

WrappedObservableSet类的大部分代码都抄自WritableSet WrappedObservableSet.java

package net.gdface.ui.binding;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;

import org.eclipse.core.databinding.observable.Diffs;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.set.ObservableSet;

/**
 * 将指定的{@link Set}对象(wrappedSet)封装为 {@link ObservableSet},<br>
 * 对observable对象的操作同步到原对象<br>
 * override方法实现代码抄自{@link org.eclipse.core.databinding.observable.set.WritableSet}
 * @author guyadong
 *
 * @param <E>
 */
public class WrappedObservableSet<E> extends ObservableSet<E>{

    /**
     * @param realm
     * @param wrappedSet 为null抛出异常
     * @param elementType
     */
    protected WrappedObservableSet(Realm realm, Set<E> wrappedSet, Object elementType) {
        super(realm, Objects.requireNonNull(wrappedSet,"the argument wrappedSet must not be null"), elementType);       
    }

    public WrappedObservableSet(Set<E> wrappedSet, Object elementType) {
        this(Realm.getDefault(),wrappedSet, elementType);
    }
    public WrappedObservableSet(Set<E> wrappedSet) {
        this(Realm.getDefault(),wrappedSet, null);
    }
    @Override
    public boolean add(E o) {
        getterCalled();
        boolean added = wrappedSet.add(o);
        if (added) {
            Set<E> removals = Collections.emptySet();
            fireSetChange(Diffs.createSetDiff(Collections.singleton(o), removals));
        }
        return added;
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        getterCalled();
        Set<E> additions = new HashSet<E>();
        Iterator<? extends E> it = c.iterator();
        while (it.hasNext()) {
            E element = it.next();
            if (wrappedSet.add(element)) {
                additions.add(element);
            }
        }
        if (additions.size() > 0) {
            Set<E> removals = Collections.emptySet();
            fireSetChange(Diffs.createSetDiff(additions, removals));
            return true;
        }
        return false;
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean remove(Object o) {
        getterCalled();
        boolean removed = wrappedSet.remove(o);
        if (removed) {
            Set<E> additions = Collections.emptySet();
            fireSetChange(Diffs.createSetDiff(additions,
                    Collections.singleton((E) o)));
        }
        return removed;
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean removeAll(Collection<?> c) {
        getterCalled();
        Set<E> removes = new HashSet<E>();
        Iterator<?> it = c.iterator();
        while (it.hasNext()) {
            Object element = it.next();
            if (wrappedSet.remove(element)) {
                removes.add((E) element);
            }
        }
        if (removes.size() > 0) {
            Set<E> additions = Collections.emptySet();
            fireSetChange(Diffs.createSetDiff(additions, removes));
            return true;
        }
        return false;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        getterCalled();
        Set<E> removes = new HashSet<E>();
        Iterator<E> it = wrappedSet.iterator();
        while (it.hasNext()) {
            E element = it.next();
            if (!c.contains(element)) {
                it.remove();
                removes.add(element);
            }
        }
        if (removes.size() > 0) {
            Set<E> additions = Collections.emptySet();
            fireSetChange(Diffs.createSetDiff(additions, removes));
            return true;
        }
        return false;
    }

    @Override
    public void clear() {
        getterCalled();
        Set<E> removes = new HashSet<E>(wrappedSet);
        Set<E> additions = Collections.emptySet();
        wrappedSet.clear();
        fireSetChange(Diffs.createSetDiff(additions, removes));
    }
}

WrappedObservableMap

WrappedObservableMap的大部分代码也都是抄自WritableMap WrappedObservableMap.java

package net.gdface.ui.binding;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.eclipse.core.databinding.observable.Diffs;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.map.MapDiff;
import org.eclipse.core.databinding.observable.map.ObservableMap;
import org.eclipse.core.internal.databinding.observable.Util;

/**
 * 将指定的{@link Map}对象(wrappedMap)封装为 {@link ObservableMap},<br>
 * 对observable对象的操作同步到原对象<br>
 * override方法实现代码抄自 {@link org.eclipse.core.databinding.observable.map.WritableMap}
 * @author guyadong
 *
 * @param <K>
 * @param <V>
 */
public class WrappedObservableMap<K, V> extends ObservableMap<K, V> {

    private final Object keyType;
    private final Object valueType;

    public WrappedObservableMap(Map<K, V> wrappedMap) {
        this(Realm.getDefault(),wrappedMap, null, null);
    }

    public WrappedObservableMap(Map<K, V> wrappedMap, Object keyType, Object valueType) {
        this(Realm.getDefault(),wrappedMap, keyType, valueType);
    }
    /**
     * @param realm
     * @param wrappedMap 为null抛出异常
     * @param keyType
     * @param valueType
     */
    public WrappedObservableMap(Realm realm, Map<K, V> wrappedMap, Object keyType, Object valueType) {
        super(realm, Objects.requireNonNull(wrappedMap,"the argument wrappedMap must not be null"));
        this.keyType=keyType;
        this.valueType=valueType;
    }

    @Override
    public Object getKeyType() {
        return keyType;
    }

    @Override
    public Object getValueType() {
        return valueType;
    }
    @Override
    public V put(K key, V value) {
        checkRealm();

        boolean containedKeyBefore = wrappedMap.containsKey(key);
        V result = wrappedMap.put(key, value);
        boolean containedKeyAfter = wrappedMap.containsKey(key);

        if (containedKeyBefore != containedKeyAfter
                || !Util.equals(result, value)) {
            MapDiff<K, V> diff;
            if (containedKeyBefore) {
                if (containedKeyAfter) {
                    diff = Diffs.createMapDiffSingleChange(key, result, value);
                } else {
                    diff = Diffs.createMapDiffSingleRemove(key, result);
                }
            } else {
                diff = Diffs.createMapDiffSingleAdd(key, value);
            }
            fireMapChange(diff);
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    @Override
    public V remove(Object key) {
        checkRealm();
        if (wrappedMap.containsKey(key)) {
            V result = wrappedMap.remove(key);
            fireMapChange(Diffs.createMapDiffSingleRemove((K) key, result));
            return result;
        }
        return null;
    }

    @Override
    public void clear() {
        checkRealm();
        if (!isEmpty()) {
            Map<K, V> copy = new HashMap<>(wrappedMap);
            wrappedMap.clear();
            fireMapChange(Diffs.createMapDiffRemoveAll(copy));
        }
    }

    @Override
    public void putAll(Map<? extends K, ? extends V> map) {
        checkRealm();
        Set<K> addedKeys = new HashSet<>(map.size());
        Map<K, V> changes = new HashMap<>(map.size());
        for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
            boolean add = !wrappedMap.containsKey(entry.getKey());
            V previousValue = wrappedMap.put(entry.getKey(), entry.getValue());
            if (add) {
                addedKeys.add(entry.getKey());
            } else {
                changes.put(entry.getKey(), previousValue);
            }
        }
        if (!addedKeys.isEmpty() || !changes.isEmpty()) {
            Set<K> removedKeys = Collections.emptySet();
            fireMapChange(Diffs.createMapDiff(addedKeys, removedKeys,
                    changes.keySet(), changes, wrappedMap));
        }
    }

}

注意

上面代码中使用了java8才有方法,所以要在java8下编译

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏俞其荣的博客

HashMap内部原理解析HeaderHashMap 必知源码分析Java 1.8 中 HashMap 的不同Footer

25710
来自专栏数据结构与算法

2879 堆的判断

2879 堆的判断 时间限制: 1 s 空间限制: 32000 KB 题目等级 : 黄金 Gold 题目描述 Description 堆是一种常用...

3148
来自专栏xiaoxi666的专栏

【模板小程序】任意长度非负十进制数转化为二进制(java实现)

1624
来自专栏向治洪

数据结构之线性表

基本概念 线性表(List):由零个或多个数据元素组成的有限序列。 特征: 1.线性表是一个序列。 2.0个元素构成的线性表是空表。 3.线性表中的第一个元素无...

1919
来自专栏DHUtoBUAA

找出该树中第二小的值--思路及算法实现

  在二叉树中最重要的操作莫过于遍历,即按照某一顺序访问树中的所有节点。二叉树的前序遍历、中序遍历、后序遍历都有递归和循环两种不同的实现方法。每种遍历的递归实现...

2955
来自专栏java一日一条

Java 容器&泛型(1):认识容器

容器是Java语言学习中重要的一部分。泥瓦匠我的感觉是刚开始挺难学的,但等你熟悉它,接触多了,也就“顺理成章”地知道了。Java的容器类主要由两个接口派生而出:...

872
来自专栏Java爬坑系列

【Java入门提高篇】Day26 Java容器类详解(八)HashSet源码分析

  前面花了好几篇的篇幅把HashMap里里外外说了个遍,大家可能对于源码分析篇已经讳莫如深了。

894
来自专栏Java爬坑系列

【Java入门提高篇】Day28 Java容器类详解(十)LinkedHashMap详解

  今天来介绍一下容器类中的另一个哈希表———》LinkedHashMap。这是HashMap的关门弟子,直接继承了HashMap的衣钵,所以拥有HashMap...

842
来自专栏我是业余自学C/C++的

用vector描述线性表

1283
来自专栏java系列博客

Java ConcurrentModificationException异常原因和解决方法

1952

扫码关注云+社区