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

org.semanticweb.owlapi.util.CollectionFactory Maven / Gradle / Ivy

/*
 * This file is part of the OWL API.
 * 
 * The contents of this file are subject to the LGPL License, Version 3.0.
 * 
 * Copyright (C) 2011, The University of Manchester
 * 
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU 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 Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this program. If
 * not, see http://www.gnu.org/licenses/.
 * 
 * 
 * Alternatively, the contents of this file may be used under the terms of the Apache License,
 * Version 2.0 in which case, the provisions of the Apache License Version 2.0 are applicable
 * instead of those above.
 * 
 * Copyright 2011, University of Manchester
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package org.semanticweb.owlapi.util;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.semanticweb.owlapi.model.OWLObject;

/**
 * @author Matthew Horridge, The University Of Manchester, Bio-Health
 *         Informatics Group, Date: 10-Jan-2007
 */
public class CollectionFactory {

    private static final Logger logger = Logger
            .getLogger(CollectionFactory.class.getName());
    private static final AtomicInteger expectedThreads = new AtomicInteger(8);

    /**
     * Sort the input collection; if the ordering is unstable and an error is
     * thrown (due to the use of TimSort in JDK 1.7 and newer), catch it and
     * leave the collection unsorted. NOTE: use this method if ordering is
     * desirable but not necessary.
     * 
     * @param toReturn
     *        list to sort
     * @param 
     *        collection type
     */
    public static > void sortOptionallyComparables(
            List toReturn) {
        try {
            Collections.sort(toReturn);
        } catch (IllegalArgumentException e) {
            // catch possible sorting misbehaviour
            if (!e.getMessage().contains(
                    "Comparison method violates its general contract!")) {
                throw e;
            }
            // otherwise print a warning and leave the list unsorted
            logger.log(Level.WARNING,
                    "Misbehaving triple comparator, leaving triples unsorted: "
                            + toReturn, e);
        }
    }

    /**
     * Sort the input collection; if the ordering is unstable and an error is
     * thrown (due to the use of TimSort in JDK 1.7 and newer), catch it and
     * leave the collection unsorted. NOTE: use this method if ordering is
     * desirable but not necessary.
     * 
     * @param toReturn
     *        list to sort
     */
    public static void sortOptionally(List toReturn) {
        try {
            Collections.sort(toReturn);
        } catch (IllegalArgumentException e) {
            // catch possible sorting misbehaviour
            if (!e.getMessage().contains(
                    "Comparison method violates its general contract!")) {
                throw e;
            }
            // otherwise print a warning and leave the list unsorted
            logger.log(Level.WARNING,
                    "Misbehaving triple comparator, leaving triples unsorted: "
                            + toReturn, e);
        }
    }

    /**
     * @param value
     *        the number of expected threads that will access threadsafe
     *        collections; useful for increasing the concurrency in
     *        ConcurrentHashMaps
     */
    public static void setExpectedThreads(int value) {
        expectedThreads.set(value);
    }

    /** @return The current number of expected threads. */
    public static int getExpectedThreads() {
        return expectedThreads.get();
    }

    /**
     * @return fresh non threadsafe set
     * @param 
     *        axiom type
     */
    public static  Set createSet() {
        // TODO large number of sets stay very small, wasting space
        return new HashSet();
    }

    /**
     * @return fresh non threadsafe list
     * @param 
     *        content type
     */
    public static  List createList() {
        return new ArrayList();
    }

    /**
     * @return fresh threadsafe list
     * @param 
     *        content type
     */
    public static  List createSyncList() {
        return new CopyOnWriteArrayList();
    }

    /**
     * @param c
     *        values to add to the set
     * @return fresh non threadsafe set
     * @param 
     *        axiom type
     */
    public static  Set createSet(Collection c) {
        return new HashSet(c);
    }

    /**
     * @param initialCapacity
     *        initial capacity for the new set
     * @return fresh non threadsafe set
     * @param 
     *        axiom type
     */
    public static  Set createSet(int initialCapacity) {
        return new HashSet(initialCapacity);
    }

    /**
     * @return fresh map
     * @param 
     *        key type
     * @param 
     *        value type
     */
    public static  Map createMap() {
        return new HashMap();
    }

    /**
     * @return a new weak hashmap wrapped as a synchronized map
     * @param 
     *        key type
     * @param 
     *        value type
     */
    public static  Map> createSyncWeakMap() {
        return Collections
                .synchronizedMap(new WeakHashMap>());
    }

    /**
     * @param elements
     *        values to add to the set
     * @return fresh non threadsafe set
     * @param 
     *        axiom type
     */
    public static  Set createSet(T... elements) {
        Set result = createSet();
        for (T t : elements) {
            result.add(t);
        }
        return result;
    }

    /**
     * @return fresh threadsafe set
     * @param 
     *        set type
     */
    public static  Set createSyncSet() {
        ConcurrentHashMap internalMap = createSyncMap();
        return Collections.newSetFromMap(internalMap);
    }

    /**
     * @return fresh threadsafe hashmap
     * @param 
     *        key type
     * @param 
     *        value type
     */
    public static  ConcurrentHashMap createSyncMap() {
        return new ConcurrentHashMap(16, 0.75F, expectedThreads.get());
    }

    /**
     * this class implements a Set using a ConcurrentHashMap as backing
     * structure; compared to the default HashSet implementation, this structure
     * is threadsafe without being completely synchronized, so it offers better
     * performances.
     * 
     * @param 
     *        type of collection
     */
    private static final class SyncSet implements Set {

        private final ConcurrentHashMap> backingMap;

        public SyncSet(ConcurrentHashMap> map) {
            backingMap = map;
        }

        public SyncSet() {
            this(new ConcurrentHashMap>());
        }

        public SyncSet(Collection delegate) {
            this();
            for (T d : delegate) {
                add(d);
            }
        }

        @Override
        public boolean add(T e) {
            return backingMap.put(e, this) == null;
        }

        @Override
        public boolean addAll(Collection c) {
            boolean toReturn = false;
            for (T o : c) {
                toReturn = toReturn || add(o);
            }
            return toReturn;
        }

        @Override
        public void clear() {
            backingMap.clear();
        }

        @Override
        public boolean contains(Object o) {
            return backingMap.containsKey(o);
        }

        @Override
        public boolean containsAll(Collection c) {
            boolean toReturn = true;
            for (Object o : c) {
                toReturn = toReturn && contains(o);
                if (!toReturn) {
                    return toReturn;
                }
            }
            return toReturn;
        }

        @Override
        public boolean isEmpty() {
            return backingMap.isEmpty();
        }

        @Override
        public Iterator iterator() {
            return backingMap.keySet().iterator();
        }

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

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

        @Override
        public boolean removeAll(Collection c) {
            boolean toReturn = false;
            for (Object o : c) {
                toReturn = toReturn || remove(o);
            }
            return toReturn;
        }

        @Override
        public boolean retainAll(Collection c) {
            boolean toReturn = false;
            for (Map.Entry> e : backingMap.entrySet()) {
                if (!c.contains(e.getKey())) {
                    toReturn = true;
                    backingMap.remove(e.getKey());
                }
            }
            return toReturn;
        }

        @Override
        public Object[] toArray() {
            return backingMap.keySet().toArray();
        }

        @Override
        public  Type[] toArray(Type[] a) {
            return backingMap.keySet().toArray(a);
        }

        @SuppressWarnings("rawtypes")
        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (this == obj) {
                return true;
            }
            if (obj instanceof SyncSet) {
                return this.backingMap.keySet().equals(
                        ((SyncSet) obj).backingMap.keySet());
            }
            if (obj instanceof Collection) {
                return new HashSet(this).equals(obj);
            }
            return false;
        }

        @Override
        public int hashCode() {
            return backingMap.hashCode();
        }
    }

    /**
     * @param source
     *        the collection to lazily copy
     * @return a lazy defensive copy for source; the source collection will not
     *         be copied until a method that modifies the collection gets
     *         called, e.g., add(), addAll()
     * @param 
     *        axiom type
     */
    public static  Set getCopyOnRequestSet(Collection source) {
        return getCopyOnRequestSetFromMutableCollection(source);
    }

    /**
     * @param source
     *        source collection
     * @return copy on request that builds a list from the input set
     * @param 
     *        axiom type
     */
    public static  Set getCopyOnRequestSetFromMutableCollection(
            Collection source) {
        if (source == null || source.isEmpty()) {
            return Collections.emptySet();
        }
        return new ConditionalCopySet(source, true);
    }

    /**
     * @param source
     *        the source collection
     * @return copy on request that does not build a list immediately
     * @param 
     *        axiom type
     */
    public static  Set getCopyOnRequestSetFromImmutableCollection(
            Collection source) {
        if (source == null || source.isEmpty()) {
            return Collections.emptySet();
        }
        return new ConditionalCopySet(source, false);
    }

    /**
     * @param source
     *        initial values
     * @return a threadsafe copy on request set
     * @param 
     *        axiom type
     */
    public static  Set getThreadSafeCopyOnRequestSet(Set source) {
        return new ThreadSafeConditionalCopySet(source);
    }

    /**
     * a set implementation that uses a delegate collection for all read-only
     * operations and makes a copy if changes are attempted. Useful for cheap
     * defensive copies: no costly rehashing on the original collection is made
     * unless changes are attempted. Changes are not mirrored back to the
     * original collection, although changes to the original set BEFORE changes
     * to the copy are reflected in the copy. If the source collection is not
     * supposed to change, then this collection behaves just like a regular
     * defensive copy; if the source collection can change, then this collection
     * should be built from a cheap copy of the original collection. For
     * example, if the source collection is a set, it can be copied into a list;
     * the cost of the copy operation from set to list is approximately 1/3 of
     * the cost of copying into a new HashSet. This is not efficient if the most
     * common operations performed on the copy are contains() or containsAll(),
     * since they are more expensive for lists wrt sets; a counter for these
     * calls is maintained by the collection, so if a large number of
     * contains/containsAll calls takes place, the delegate is turned into a
     * regular set. This implementation is not threadsafe even if the source set
     * is: there is no lock during the copy, and the new set is not threadsafe.
     * 
     * @param 
     *        the type contained
     */
    public static class ConditionalCopySet implements Set {

        private static final int maxContains = 10;
        private boolean copyDone = false;
        protected Collection delegate;
        private int containsCounter = 0;

        /**
         * @param source
         *        initial elements
         * @param listCopy
         *        true if a copy must be made
         */
        public ConditionalCopySet(Collection source, boolean listCopy) {
            if (listCopy) {
                this.delegate = new ArrayList(source);
            } else {
                this.delegate = source;
            }
        }

        @SuppressWarnings("rawtypes")
        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (this == obj) {
                return true;
            }
            if (obj instanceof ConditionalCopySet) {
                return delegate
                        .containsAll(((ConditionalCopySet) obj).delegate)
                        && ((ConditionalCopySet) obj).delegate
                                .containsAll(delegate);
            }
            if (obj instanceof Collection) {
                return delegate.containsAll((Collection) obj)
                        && ((Collection) obj).containsAll(delegate);
            }
            return false;
        }

        @Override
        public int hashCode() {
            return delegate.hashCode();
        }

        @Override
        public String toString() {
            return delegate.toString();
        }

        @Override
        public boolean add(T arg0) {
            if (!copyDone) {
                copyDone = true;
                delegate = new LinkedHashSet(delegate);
            }
            return delegate.add(arg0);
        }

        @Override
        public boolean addAll(Collection arg0) {
            if (!copyDone) {
                copyDone = true;
                delegate = new LinkedHashSet(delegate);
            }
            return delegate.addAll(arg0);
        }

        @Override
        public void clear() {
            if (!copyDone) {
                copyDone = true;
                delegate = new LinkedHashSet();
            }
            delegate.clear();
        }

        @Override
        public boolean contains(Object arg0) {
            containsCounter++;
            if (containsCounter >= maxContains && !copyDone) {
                // many calls to contains, inefficient if the delegate is not a
                // set
                if (!(delegate instanceof Set)) {
                    copyDone = true;
                    delegate = new LinkedHashSet(delegate);
                }
            }
            return delegate.contains(arg0);
        }

        @Override
        public boolean containsAll(Collection arg0) {
            containsCounter++;
            if (containsCounter >= maxContains && !copyDone) {
                // many calls to contains, inefficient if the delegate is not a
                // set
                if (!(delegate instanceof Set)) {
                    copyDone = true;
                    delegate = new LinkedHashSet(delegate);
                }
            }
            return delegate.containsAll(arg0);
        }

        @Override
        public boolean isEmpty() {
            return delegate.isEmpty();
        }

        @Override
        public Iterator iterator() {
            return delegate.iterator();
        }

        @Override
        public boolean remove(Object arg0) {
            if (!copyDone) {
                copyDone = true;
                delegate = new LinkedHashSet(delegate);
            }
            return delegate.remove(arg0);
        }

        @Override
        public boolean removeAll(Collection arg0) {
            if (!copyDone) {
                copyDone = true;
                delegate = new LinkedHashSet(delegate);
            }
            return delegate.removeAll(arg0);
        }

        @Override
        public boolean retainAll(Collection arg0) {
            if (!copyDone) {
                copyDone = true;
                delegate = new LinkedHashSet(delegate);
            }
            return delegate.retainAll(arg0);
        }

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

        @Override
        public Object[] toArray() {
            return delegate.toArray();
        }

        @Override
        public  Type[] toArray(Type[] arg0) {
            return delegate.toArray(arg0);
        }
    }

    /**
     * this class behaves like ConditionalCopySet except it is designed to be
     * threadsafe; multiple thread access is regulated by a readwritelock;
     * modifications will create a copy based on SyncSet.
     * 
     * @param 
     *        the type contained
     */
    public static class ThreadSafeConditionalCopySet implements Set {

        private static final int maxContains = 10;
        private final AtomicBoolean copyDone = new AtomicBoolean(false);
        private Collection delegate;
        private final ReadWriteLock lock = new ReentrantReadWriteLock();
        private final Lock readLock = lock.readLock();
        private final Lock writeLock = lock.writeLock();
        private final AtomicInteger containsCounter = new AtomicInteger(0);

        /**
         * @param source
         *        initial values
         */
        public ThreadSafeConditionalCopySet(Collection source) {
            this.delegate = new ArrayList(source);
        }

        @Override
        public boolean equals(Object obj) {
            try {
                readLock.lock();
                if (obj == null) {
                    return false;
                }
                if (this == obj) {
                    return true;
                }
                if (obj instanceof ConditionalCopySet) {
                    return delegate
                            .equals(((ConditionalCopySet) obj).delegate);
                }
                if (obj instanceof Set) {
                    return delegate.equals(obj);
                }
                return false;
            } finally {
                readLock.unlock();
            }
        }

        @Override
        public int hashCode() {
            try {
                readLock.lock();
                return delegate.hashCode();
            } finally {
                readLock.unlock();
            }
        }

        @Override
        public boolean add(T arg0) {
            try {
                writeLock.lock();
                if (!copyDone.getAndSet(true)) {
                    delegate = new SyncSet(delegate);
                }
                return delegate.add(arg0);
            } finally {
                writeLock.unlock();
            }
        }

        @Override
        public boolean addAll(Collection arg0) {
            try {
                writeLock.lock();
                if (!copyDone.getAndSet(true)) {
                    delegate = new SyncSet(delegate);
                }
                return delegate.addAll(arg0);
            } finally {
                writeLock.unlock();
            }
        }

        @Override
        public void clear() {
            try {
                writeLock.lock();
                if (!copyDone.getAndSet(true)) {
                    delegate = new SyncSet();
                }
                delegate.clear();
            } finally {
                writeLock.unlock();
            }
        }

        @Override
        public boolean contains(Object arg0) {
            /*
             * note: the check on the number of contains and on the copyDone
             * flag is intentionally not made inside a lock; these operations
             * will not stop other threads from accessing the collection, and
             * the lack of synchronization means that a few contains/containsAll
             * calls might not be recorded; the result is that the copy
             * optimization would be delayed. This is not an issue, since
             * maxContains is only a rough estimate.
             */
            if (containsCounter.incrementAndGet() >= maxContains
                    && !copyDone.get()) {
                try {
                    writeLock.lock();
                    // many calls to contains, inefficient if the delegate
                    // is not a set
                    // copyDone is doublechecked, but here it's protected by
                    // the write
                    // lock as in all other instances in which its value is
                    // changed
                    if (!(delegate instanceof Set)) {
                        if (!copyDone.getAndSet(true)) {
                            delegate = new SyncSet(delegate);
                        }
                    }
                    // skip the second portion of the method: no need to
                    // reacquire
                    // the lock, it's already a write lock
                    return delegate.contains(arg0);
                } finally {
                    writeLock.unlock();
                }
            }
            try {
                readLock.lock();
                return delegate.contains(arg0);
            } finally {
                readLock.unlock();
            }
        }

        @Override
        public boolean containsAll(Collection arg0) {
            if (containsCounter.incrementAndGet() >= maxContains
                    && !copyDone.get()) {
                try {
                    writeLock.lock();
                    // many calls to contains, inefficient if the delegate
                    // is not a set
                    // copyDone is doublechecked, but here it's protected by
                    // the write
                    // lock as in all other instances in which its value is
                    // changed
                    if (!(delegate instanceof Set)) {
                        if (!copyDone.getAndSet(true)) {
                            delegate = new SyncSet(delegate);
                        }
                    }
                    // skip the second portion of the method: no need to
                    // reacquire
                    // the lock, it's already a write lock
                    return delegate.containsAll(arg0);
                } finally {
                    writeLock.unlock();
                }
            }
            try {
                readLock.lock();
                return delegate.containsAll(arg0);
            } finally {
                readLock.unlock();
            }
        }

        @Override
        public boolean isEmpty() {
            try {
                readLock.lock();
                return delegate.isEmpty();
            } finally {
                readLock.unlock();
            }
        }

        @Override
        public Iterator iterator() {
            try {
                readLock.lock();
                return delegate.iterator();
            } finally {
                readLock.unlock();
            }
        }

        @Override
        public boolean remove(Object arg0) {
            try {
                writeLock.lock();
                if (!copyDone.getAndSet(true)) {
                    delegate = new SyncSet(delegate);
                }
                return delegate.remove(arg0);
            } finally {
                writeLock.unlock();
            }
        }

        @Override
        public boolean removeAll(Collection arg0) {
            try {
                writeLock.lock();
                if (!copyDone.getAndSet(true)) {
                    delegate = new SyncSet(delegate);
                }
                return delegate.removeAll(arg0);
            } finally {
                writeLock.unlock();
            }
        }

        @Override
        public boolean retainAll(Collection arg0) {
            try {
                writeLock.lock();
                if (!copyDone.getAndSet(true)) {
                    delegate = new SyncSet(delegate);
                }
                return delegate.retainAll(arg0);
            } finally {
                writeLock.unlock();
            }
        }

        @Override
        public int size() {
            try {
                readLock.lock();
                return delegate.size();
            } finally {
                readLock.unlock();
            }
        }

        @Override
        public Object[] toArray() {
            try {
                readLock.lock();
                return delegate.toArray();
            } finally {
                readLock.unlock();
            }
        }

        @Override
        public  Type[] toArray(Type[] arg0) {
            try {
                readLock.lock();
                return delegate.toArray(arg0);
            } finally {
                readLock.unlock();
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy