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

hm.binkley.util.OrderedProperties Maven / Gradle / Ivy

There is a newer version: 6
Show newest version
/*
 * This is free and unencumbered software released into the public domain.
 *
 * Please see https://github.com/binkley/binkley/blob/master/LICENSE.md.
 */

package hm.binkley.util;

import javax.annotation.Nonnull;
import java.util.AbstractCollection;
import java.util.AbstractMap.SimpleEntry;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;

import static hm.binkley.util.OrderedProperties.Ordering.DEFINED;
import static java.util.Collections.enumeration;

/**
 * {@code OrderedProperties} needs documentation.
 *
 * @author B. K. Oxley (binkley)
 * @todo Needs documentation.
 */
public class OrderedProperties
        extends Properties {
    public enum Ordering {
        DEFINED {
            @Override
            protected Set from() {
                return new LinkedHashSet<>();
            }
        }, UPDATED {
            @Override
            protected Set from() {
                return new UpdatedSet<>();
            }
        }, NATURAL {
            @Override
            protected Set from() {
                return new TreeSet<>();
            }
        };

        protected abstract Set from();

    }

    private final Set keys;

    public OrderedProperties() {
        this(DEFINED);
    }

    public OrderedProperties(@Nonnull final Ordering ordering) {
        this(ordering.from());
    }

    public OrderedProperties(@Nonnull final Comparator ordering) {
        this(new TreeSet<>(ordering));
    }

    private OrderedProperties(final Set keys) {
        this.keys = keys;
    }

    @Override
    public Enumeration propertyNames() {
        return enumeration(keySet());
    }

    @Override
    @SuppressWarnings("unchecked")
    public Set stringPropertyNames() {
        return (Set) keySet();
    }

    @Override
    public synchronized Enumeration keys() {
        return enumeration(keySet());
    }

    @Override
    public synchronized Enumeration elements() {
        return enumeration(values());
    }

    @Override
    public synchronized Object put(final Object key, final Object value) {
        keys.add(key);
        return super.put(key, value);
    }

    @Override
    public synchronized Object remove(final Object key) {
        keys.remove(key);
        return super.remove(key);
    }

    @Nonnull
    @Override
    public Set keySet() {
        return new Keys();
    }

    @Nonnull
    @Override
    public Set> entrySet() {
        return new Entries();
    }

    @Nonnull
    @Override
    public Collection values() {
        return new Values();
    }

    private static final class UpdatedSet
            extends LinkedHashSet {
        @Override
        public boolean add(final T element) {
            final boolean found = remove(element);
            super.add(element);
            return found;
        }
    }

    private final class Keys
            extends AbstractSet {
        @Nonnull
        @Override
        public Iterator iterator() {
            return new KeyIterator();
        }

        @Override
        public int size() {
            return OrderedProperties.this.size();
        }

        @Override
        public boolean remove(final Object o) {
            return null != OrderedProperties.this.remove(o);
        }
    }

    private final class KeyIterator
            implements Iterator {
        private final Iterator kit = keys.iterator();
        private Object key;

        @Override
        public boolean hasNext() {
            return kit.hasNext();
        }

        @Override
        public Object next() {
            return key = kit.next();
        }

        @Override
        public void remove() {
            OrderedProperties.this.remove(key);
        }
    }

    private final class Entries
            extends AbstractSet> {
        @Nonnull
        @Override
        public Iterator> iterator() {
            return new EntryIterator();
        }

        @Override
        public int size() {
            return OrderedProperties.this.size();
        }

        @Override
        public boolean add(final Entry entry) {
            final Object newValue = entry.getValue();
            return Objects.equals(newValue, put(entry.getKey(), newValue));
        }

        @Override
        public boolean remove(final Object o) {
            if (!(o instanceof Entry))
                return false;
            final Entry entry = (Entry) o;
            return null != OrderedProperties.this.remove(entry.getKey());
        }
    }

    private final class EntryIterator
            implements Iterator> {
        private final Iterator kit = keys.iterator();
        private Object key;

        @Override
        public boolean hasNext() {
            return kit.hasNext();
        }

        @Override
        public Entry next() {
            key = kit.next();
            return new SimpleEntry<>(key, get(key));
        }

        @Override
        public void remove() {
            OrderedProperties.this.remove(key);
        }
    }

    private final class Values
            extends AbstractCollection {
        @Nonnull
        @Override
        public Iterator iterator() {
            return new ValueIterator();
        }

        @Override
        public int size() {
            return 0;
        }

        @Override
        public boolean remove(final Object o) {
            for (final Object key : keys)
                if (Objects.equals(o, get(key))) {
                    OrderedProperties.this.remove(key);
                    return true;
                }
            return false;
        }
    }

    private final class ValueIterator
            implements Iterator {
        private final Iterator kit = keys.iterator();
        private Object key;

        @Override
        public boolean hasNext() {
            return kit.hasNext();
        }

        @Override
        public Object next() {
            key = kit.next();
            return get(key);
        }

        @Override
        public void remove() {
            OrderedProperties.this.remove(key);
        }
    }
}