Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.modeshape.schematic.internal.document.BasicArray Maven / Gradle / Ivy
Go to download
API for storing JSON/BSON documents and JSON Schemas support
/*
* ModeShape (http://www.modeshape.org)
*
* 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.modeshape.schematic.internal.document;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.modeshape.schematic.annotation.Immutable;
import org.modeshape.schematic.document.Array;
import org.modeshape.schematic.document.Binary;
import org.modeshape.schematic.document.Bson;
import org.modeshape.schematic.document.Code;
import org.modeshape.schematic.document.CodeWithScope;
import org.modeshape.schematic.document.Document;
import org.modeshape.schematic.document.Json;
import org.modeshape.schematic.document.MaxKey;
import org.modeshape.schematic.document.MinKey;
import org.modeshape.schematic.document.Null;
import org.modeshape.schematic.document.ObjectId;
import org.modeshape.schematic.document.Symbol;
import org.modeshape.schematic.internal.schema.DocumentTransformer;
/**
* A {@link Bson.Type#ARRAY ordered array of values} for use as a value within a
* {@link Document BSON Object}. Instances of this type are designed to be unmodifiable from a client's perspective, since clients
* always modify the instances using an editor. There are several internal*
methods that do modify the contents, but
* these may not be used by client applications.
*
* Since BSON and JSON documents can be simple arrays of values, this class implements the {@link Document} interface, where the
* object's names are expected to be string values of integer indexes. This class also implements {@link List} interface, but only
* supports the read methods.
*
*
* @author Randall Hauch (C) 2011 Red Hat Inc.
*/
public class BasicArray implements MutableArray {
private static final long serialVersionUID = 1L;
private final List values;
public BasicArray() {
this.values = new ArrayList<>();
}
public BasicArray( int initialCapacity ) {
this.values = initialCapacity > 0 ? new ArrayList<>(initialCapacity) : new ArrayList<>();
}
public BasicArray( List values ) {
this.values = values;
}
public BasicArray( Object... values ) {
this.values = new ArrayList<>(Arrays.asList(values));
}
@Override
public Object get( String name ) {
int index = indexFrom(name);
return isValidIndex(index) ? values.get(index) : null;
}
@Override
public boolean containsField( String name ) {
int index = indexFrom(name);
return isValidIndex(index);
}
@Override
public boolean containsAll( Document document ) {
if (document instanceof Array) {
return containsAll((List>)document);
}
if (document != null) {
for (Field field : document.fields()) {
Object thisValue = get(field.getName());
Object thatValue = field.getValue();
if (!BsonUtils.valuesAreEqual(thisValue, thatValue)) {
return false;
}
}
}
return true;
}
@Override
public Set keySet() {
return new IndexSequence(size());
}
@Override
public Map toMap() {
Map result = new HashMap<>();
int i = 0;
for (String index : keySet()) {
result.put(index, values.get(i++)); // we know that keySet().iterator() is ordered
}
return result;
}
@Override
public Iterable fields() {
return () -> {
final Iterator indexIter = IndexSequence.infiniteSequence();
final Iterator valueIter = values.iterator();
return new Iterator() {
@Override
public boolean hasNext() {
return valueIter.hasNext();
}
@Override
public Field next() {
return new ImmutableField(indexIter.next(), valueIter.next());
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
};
}
@Override
public int size() {
return values.size();
}
@Override
public boolean contains( Object o ) {
return values.contains(o);
}
@Override
public boolean containsAll( Collection> c ) {
return values.containsAll(c);
}
@Override
public Object get( int index ) {
return values.get(index);
}
@Override
public int hashCode() {
return values.hashCode();
}
@Override
public boolean equals( Object obj ) {
if (this == obj) {
return true;
}
if (obj instanceof Iterable) {
Iterable> that = (Iterable>)obj;
Iterator> thisIter = values.iterator();
Iterator> thatIter = null;
if (obj instanceof List) {
List> thatList = (List>)that;
if (this.size() != thatList.size()) {
return false;
}
if (thatList instanceof BasicArray) {
thatIter = ((BasicArray)thatList).values.iterator();
} else {
thatIter = that.iterator();
}
}
assert thatIter != null;
while (thisIter.hasNext() && thatIter.hasNext()) {
Object thisValue = thisIter.next();
Object thatValue = thatIter.next();
if (!BsonUtils.valuesAreEqual(thisValue, thatValue)) {
return false;
}
}
return !thisIter.hasNext() && !thatIter.hasNext();
}
if (obj.getClass().isArray()) {
if (this.size() != java.lang.reflect.Array.getLength(obj)) {
return false;
}
Iterator> thisIter = values.iterator();
int index = 0;
while (thisIter.hasNext()) {
Object thisValue = thisIter.next();
Object thatValue = java.lang.reflect.Array.get(obj, index++);
if (!BsonUtils.valuesAreEqual(thisValue, thatValue)) {
return false;
}
}
return true;
}
return false;
}
@Override
public String toString() {
return Json.write(this);
}
@Override
public int indexOf( Object o ) {
return values.indexOf(o);
}
@Override
public boolean isEmpty() {
return values.isEmpty();
}
@Override
public int lastIndexOf( Object o ) {
return values.lastIndexOf(o);
}
@Override
public List subList( int fromIndex,
int toIndex ) {
return new BasicArray(values.subList(fromIndex, toIndex));
}
@Override
public Object[] toArray() {
return values.toArray();
}
@Override
public T[] toArray( T[] a ) {
return values.toArray(a);
}
@Override
public Iterator iterator() {
final Iterator delegate = values.iterator();
return new Iterator() {
@Override
public boolean hasNext() {
return delegate.hasNext();
}
@Override
public Object next() {
return delegate.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public Iterable getEntries() {
return () -> new Iterator() {
@SuppressWarnings( "synthetic-access" )
private final Iterator valueIter = BasicArray.this.values.iterator();
private int index = 0;
@Override
public boolean hasNext() {
return valueIter.hasNext();
}
@Override
public Entry next() {
Object value = valueIter.next();
return new BasicEntry(index++, value);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public Boolean getBoolean( String name ) {
Object value = get(name);
return (value instanceof Boolean) ? (Boolean)value : null;
}
@Override
public boolean getBoolean( String name,
boolean defaultValue ) {
Object value = get(name);
return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : defaultValue;
}
@Override
public Integer getInteger( String name ) {
Object value = get(name);
return (value instanceof Integer) ? (Integer)value : null;
}
@Override
public int getInteger( String name,
int defaultValue ) {
Object value = get(name);
return (value instanceof Integer) ? ((Integer)value).intValue() : defaultValue;
}
@Override
public Long getLong( String name ) {
Object value = get(name);
if (value instanceof Long) return (Long)value;
if (value instanceof Integer) return new Long(((Integer)value).longValue());
return null;
}
@Override
public long getLong( String name,
long defaultValue ) {
Object value = get(name);
if (value instanceof Long) return ((Long)value).longValue();
if (value instanceof Integer) return ((Integer)value).longValue();
return defaultValue;
}
@Override
public Double getDouble( String name ) {
Object value = get(name);
return (value instanceof Double) ? (Double)value : null;
}
@Override
public double getDouble( String name,
double defaultValue ) {
Object value = get(name);
return (value instanceof Double) ? ((Double)value).doubleValue() : defaultValue;
}
@Override
public Number getNumber( String name ) {
Object value = get(name);
return (value instanceof Number) ? (Number)value : null;
}
@Override
public Number getNumber( String name,
Number defaultValue ) {
Object value = get(name);
return (value instanceof Number) ? (Number)value : defaultValue;
}
@Override
public String getString( String name ) {
return getString(name, null);
}
@Override
public String getString( String name,
String defaultValue ) {
Object value = get(name);
if (value != null) {
if (value instanceof String) {
return (String)value;
}
if (value instanceof Symbol) {
return ((Symbol)value).getSymbol();
}
}
return defaultValue;
}
@Override
public List> getArray( String name ) {
Object value = get(name);
return (value instanceof List) ? (List>)value : null;
}
@Override
public Document getDocument( String name ) {
Object value = get(name);
return (value instanceof Document) ? (Document)value : null;
}
@Override
public boolean isNull( String name ) {
return get(name) instanceof Null;
}
@Override
public boolean isNullOrMissing( String name ) {
return Null.matches(get(name));
}
@Override
public MaxKey getMaxKey( String name ) {
Object value = get(name);
return (value instanceof MaxKey) ? (MaxKey)value : null;
}
@Override
public MinKey getMinKey( String name ) {
Object value = get(name);
return (value instanceof MinKey) ? (MinKey)value : null;
}
@Override
public Code getCode( String name ) {
Object value = get(name);
return (value instanceof Code) ? (Code)value : null;
}
@Override
public CodeWithScope getCodeWithScope( String name ) {
Object value = get(name);
return (value instanceof CodeWithScope) ? (CodeWithScope)value : null;
}
@Override
public ObjectId getObjectId( String name ) {
Object value = get(name);
return (value instanceof ObjectId) ? (ObjectId)value : null;
}
@Override
public Binary getBinary( String name ) {
Object value = get(name);
return (value instanceof Binary) ? (Binary)value : null;
}
@Override
public Date getDate(String name) {
Object value = get(name);
return (value instanceof Date) ? (Date) value : null;
}
@Override
public Symbol getSymbol( String name ) {
Object value = get(name);
if (value != null) {
if (value instanceof Symbol) {
return (Symbol)value;
}
if (value instanceof String) {
return new Symbol((String)value);
}
}
return null;
}
@Override
public Pattern getPattern( String name ) {
Object value = get(name);
return (value instanceof Pattern) ? (Pattern)value : null;
}
@Override
public UUID getUuid( String name ) {
return getUuid(name, null);
}
@Override
public UUID getUuid( String name,
UUID defaultValue ) {
Object value = get(name);
if (value != null) {
if (value instanceof UUID) {
return (UUID)value;
}
if (value instanceof String) {
try {
return UUID.fromString((String)value);
} catch (IllegalArgumentException e) {
// do nothing ...
}
}
}
return defaultValue;
}
@Override
public int getType( String name ) {
return Bson.getTypeForValue(get(name));
}
@Override
public ListIterator listIterator() {
return new UnmodifiableListIterator(values.listIterator());
}
@Override
public ListIterator listIterator( int index ) {
return new UnmodifiableListIterator(values.listIterator(index));
}
protected static final class UnmodifiableListIterator implements ListIterator {
private final ListIterator delegate;
protected UnmodifiableListIterator( ListIterator delegate ) {
this.delegate = delegate;
}
@Override
public boolean hasNext() {
return delegate.hasNext();
}
@Override
public Object next() {
return delegate.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void add( Object e ) {
throw new UnsupportedOperationException();
}
@Override
public boolean hasPrevious() {
return delegate.hasPrevious();
}
@Override
public int nextIndex() {
return delegate.nextIndex();
}
@Override
public Object previous() {
return delegate.previous();
}
@Override
public int previousIndex() {
return delegate.previousIndex();
}
@Override
public void set( Object e ) {
throw new UnsupportedOperationException();
}
}
@Override
public void add( int index,
Object element ) {
throw new UnsupportedOperationException();
}
@Override
public boolean add( Object e ) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll( Collection> c ) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll( int index,
Collection> c ) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public Object remove( int index ) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove( Object o ) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll( Collection> c ) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll( Collection> c ) {
throw new UnsupportedOperationException();
}
@Override
public Object set( int index,
Object element ) {
throw new UnsupportedOperationException();
}
protected final int indexFrom( String name ) {
return Integer.parseInt(name);
}
protected final boolean isValidIndex( int index ) {
return index >= 0 && index < size();
}
// ---------------------------------------------------------------------------------------------------------
// Mutation methods, for use only by the editor framework
// ---------------------------------------------------------------------------------------------------------
protected Object unwrap( Object value ) {
if (value instanceof DocumentEditor) {
return unwrap(((DocumentEditor)value).unwrap());
}
if (value instanceof ArrayEditor) {
return unwrap(((ArrayEditor)value).unwrap());
}
return value;
}
@Override
public boolean addValueIfAbsent( Object value ) {
value = unwrap(value);
return !this.values.contains(value) ? this.values.add(value) : false;
}
@Override
public int addValue( Object value ) {
value = unwrap(value);
int index = this.values.size();
this.values.add(index, value);
return index;
}
@Override
public void addValue( int index,
Object value ) {
value = unwrap(value);
this.values.add(index, value);
}
@Override
public Object setValue( int index,
Object value ) {
value = unwrap(value);
return this.values.set(index, value);
}
@Override
public boolean removeValue( Object value ) {
value = unwrap(value);
return this.values.remove(value);
}
@Override
public Object removeValue( int index ) {
return values.remove(index);
}
@Override
public boolean addAllValues( Collection> values ) {
if (values == null || values.isEmpty()) return false;
this.values.addAll(values.stream().map(this::unwrap).collect(Collectors.toList()));
return true;
}
@Override
public boolean addAllValues( int index,
Collection> values ) {
if (values == null || values.isEmpty()) return false;
for (Object value : values) {
this.values.add(index, unwrap(value));
}
return true;
}
@Override
public List removeAllValues( Collection> valuesToBeRemoved ) {
return removeValues(valuesToBeRemoved, true);
}
@Override
public List retainAllValues( Collection> valuesToBeRetained ) {
return removeValues(valuesToBeRetained, false);
}
/**
* Remove some of the values in this array.
*
* @param values the values to be compared to this array's values
* @param ifMatch true if this method should retain all values that match the supplied values, or false if this method should
* remove all values that match the supplied values
* @return the entries that were removed; never null
*/
private List removeValues( Collection> values,
boolean ifMatch ) {
LinkedList results = null;
// Record the list of entries that are removed, but start at the end of the values (so the indexes are correct)
ListIterator> iter = this.values.listIterator(size());
while (iter.hasPrevious()) {
int index = iter.previousIndex();
Object value = iter.previous();
if (ifMatch == values.contains(value)) {
iter.remove();
if (results == null) {
results = new LinkedList<>();
}
results.addFirst(new BasicEntry(index, value));
}
}
return results != null ? results : Collections.emptyList();
}
@Immutable
public static final class BasicEntry implements Entry {
private final int index;
private final Object value;
public BasicEntry( int index,
Object value ) {
this.index = index;
this.value = value;
}
@Override
public int getIndex() {
return index;
}
@Override
public Object getValue() {
return value;
}
@Override
public int compareTo( Entry o ) {
return o == this ? 0 : o == null ? 1 : o.getIndex() - this.getIndex();
}
}
@Override
public Object remove( String name ) {
try {
int index = indexFrom(name);
return isValidIndex(index) ? values.remove(index) : null;
} catch (NumberFormatException e) {
// Must be a value ...
return removeValue(name);
}
}
@Override
public void removeAll() {
values.clear();
}
@Override
public Object put( String name,
Object value ) {
return put(indexFrom(name), value);
}
protected final Object put( int index,
Object value ) {
final int size = size();
if (index == size) {
values.add(unwrap(value));
return value;
}
return values.set(index, unwrap(value)); // may throw IndexOutOfBoundsException
}
@Override
public void putAll( Document object ) {
if (object instanceof BasicArray) {
BasicArray that = (BasicArray)object;
addAll(that.values);
}
}
@Override
public void putAll( Map extends String, ?> map ) {
// Attempt to convert all of the keys to integers ...
List sortableEntries = new ArrayList<>(map.size());
for (Map.Entry extends String, ?> entry : map.entrySet()) {
int index = indexFrom(entry.getKey());
sortableEntries.add(new IndexEntry(index, entry.getValue()));
}
Collections.sort(sortableEntries);
// Now add them in increasing order ...
for (IndexEntry entry : sortableEntries) {
put(entry.index, unwrap(entry.value));
}
}
@Override
public MutableArray clone() {
BasicArray clone = new BasicArray();
for (Object value : this) {
value = unwrap(value);
if (value instanceof Array) {
value = ((Array)value).clone();
} else if (value instanceof Document) {
value = ((Document)value).clone();
}// every other kind of value is immutable
clone.addValue(value);
}
return clone;
}
@Override
public Array with( Map changedFields ) {
BasicArray clone = new BasicArray();
for (Field field : this.fields()) {
String name = field.getName();
Object newValue = unwrap(changedFields.get(name));
if (newValue != null) {
clone.put(name, newValue);
} else {
Object oldValue = field.getValue();
clone.put(name, oldValue);
}
}
return clone;
}
@Override
public Document with( String fieldName,
Object newValue ) {
newValue = unwrap(newValue);
BasicArray clone = new BasicArray();
for (Field field : this.fields()) {
String name = field.getName();
if (name.equals(fieldName)) {
clone.put(name, newValue);
} else {
Object oldValue = field.getValue();
clone.put(name, oldValue);
}
}
return clone;
}
@Override
public Array with( ValueTransformer transformer ) {
boolean transformed = false;
BasicArray clone = new BasicArray();
for (Field field : this.fields()) {
String name = field.getName();
Object oldValue = field.getValue();
Object newValue = null;
if (oldValue instanceof Document) {
newValue = ((Document)oldValue).with(transformer);
} else {
newValue = transformer.transform(name, oldValue);
}
if (newValue != oldValue) transformed = true;
clone.put(name, unwrap(newValue));
}
return transformed ? clone : this;
}
@Override
public Array withVariablesReplaced( Properties properties ) {
return with(new DocumentTransformer.PropertiesTransformer(properties));
}
@Override
public Array withVariablesReplacedWithSystemProperties() {
return with(new DocumentTransformer.SystemPropertiesTransformer());
}
@Immutable
protected static final class IndexEntry implements Comparable {
protected final int index;
protected final Object value;
protected IndexEntry( int index,
Object value ) {
this.index = index;
this.value = value;
}
@Override
public int compareTo( IndexEntry that ) {
return this.index - that.index;
}
@Override
public int hashCode() {
return index;
}
@Override
public boolean equals( Object obj ) {
if (obj == this) return true;
if (obj instanceof IndexEntry) {
IndexEntry that = (IndexEntry)obj;
if (this.index != that.index) return false;
if (this.value == null) return that.value == null;
return this.value.equals(that.value);
}
return false;
}
@Override
public String toString() {
return "[" + index + ',' + value + ']';
}
}
}