001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.modeshape.schematic.internal.document;
017
018import java.util.ArrayList;
019import java.util.Arrays;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.Date;
023import java.util.HashMap;
024import java.util.Iterator;
025import java.util.LinkedList;
026import java.util.List;
027import java.util.ListIterator;
028import java.util.Map;
029import java.util.Properties;
030import java.util.Set;
031import java.util.UUID;
032import java.util.regex.Pattern;
033import java.util.stream.Collectors;
034import org.modeshape.schematic.annotation.Immutable;
035import org.modeshape.schematic.document.Array;
036import org.modeshape.schematic.document.Binary;
037import org.modeshape.schematic.document.Bson;
038import org.modeshape.schematic.document.Code;
039import org.modeshape.schematic.document.CodeWithScope;
040import org.modeshape.schematic.document.Document;
041import org.modeshape.schematic.document.Json;
042import org.modeshape.schematic.document.MaxKey;
043import org.modeshape.schematic.document.MinKey;
044import org.modeshape.schematic.document.Null;
045import org.modeshape.schematic.document.ObjectId;
046import org.modeshape.schematic.document.Symbol;
047import org.modeshape.schematic.internal.schema.DocumentTransformer;
048
049/**
050 * A {@link Bson.Type#ARRAY ordered array of values} for use as a value within a
051 * {@link Document BSON Object}. Instances of this type are designed to be unmodifiable from a client's perspective, since clients
052 * always modify the instances using an editor. There are several <code>internal*</code> methods that do modify the contents, but
053 * these may not be used by client applications.
054 * <p>
055 * Since BSON and JSON documents can be simple arrays of values, this class implements the {@link Document} interface, where the
056 * object's names are expected to be string values of integer indexes. This class also implements {@link List} interface, but only
057 * supports the read methods.
058 * </p>
059 * 
060 * @author Randall Hauch <rhauch@redhat.com> (C) 2011 Red Hat Inc.
061 */
062public class BasicArray implements MutableArray {
063
064    private static final long serialVersionUID = 1L;
065
066    private final List<Object> values;
067
068    public BasicArray() {
069        this.values = new ArrayList<>();
070    }
071
072    public BasicArray( int initialCapacity ) {
073        this.values = initialCapacity > 0 ? new ArrayList<>(initialCapacity) : new ArrayList<>();
074    }
075
076    public BasicArray( List<Object> values ) {
077        this.values = values;
078    }
079
080    public BasicArray( Object... values ) {
081        this.values = new ArrayList<>(Arrays.asList(values));
082    }
083
084    @Override
085    public Object get( String name ) {
086        int index = indexFrom(name);
087        return isValidIndex(index) ? values.get(index) : null;
088    }
089
090    @Override
091    public boolean containsField( String name ) {
092        int index = indexFrom(name);
093        return isValidIndex(index);
094    }
095
096    @Override
097    public boolean containsAll( Document document ) {
098        if (document instanceof Array) {
099            return containsAll((List<?>)document);
100        }
101        if (document != null) {
102            for (Field field : document.fields()) {
103                Object thisValue = get(field.getName());
104                Object thatValue = field.getValue();
105                if (!BsonUtils.valuesAreEqual(thisValue, thatValue)) {
106                    return false;
107                }
108            }
109        }
110        return true;
111    }
112
113    @Override
114    public Set<String> keySet() {
115        return new IndexSequence(size());
116    }
117
118    @Override
119    public Map<String, ?> toMap() {
120        Map<String, Object> result = new HashMap<>();
121        int i = 0;
122        for (String index : keySet()) {
123            result.put(index, values.get(i++)); // we know that keySet().iterator() is ordered
124        }
125        return result;
126    }
127
128    @Override
129    public Iterable<Field> fields() {
130        return () -> {
131            final Iterator<String> indexIter = IndexSequence.infiniteSequence();
132            final Iterator<Object> valueIter = values.iterator();
133            return new Iterator<Field>() {
134                @Override
135                public boolean hasNext() {
136                    return valueIter.hasNext();
137                }
138
139                @Override
140                public Field next() {
141                    return new ImmutableField(indexIter.next(), valueIter.next());
142                }
143
144                @Override
145                public void remove() {
146                    throw new UnsupportedOperationException();
147                }
148            };
149        };
150    }
151
152    @Override
153    public int size() {
154        return values.size();
155    }
156
157    @Override
158    public boolean contains( Object o ) {
159        return values.contains(o);
160    }
161
162    @Override
163    public boolean containsAll( Collection<?> c ) {
164        return values.containsAll(c);
165    }
166
167    @Override
168    public Object get( int index ) {
169        return values.get(index);
170    }
171
172    @Override
173    public int hashCode() {
174        return values.hashCode();
175    }
176
177    @Override
178    public boolean equals( Object obj ) {
179        if (this == obj) {
180            return true;
181        }
182        if (obj instanceof Iterable) {
183            Iterable<?> that = (Iterable<?>)obj;
184            Iterator<?> thisIter = values.iterator();
185            Iterator<?> thatIter = null;
186            if (obj instanceof List) {
187                List<?> thatList = (List<?>)that;
188                if (this.size() != thatList.size()) {
189                    return false;
190                }
191                if (thatList instanceof BasicArray) {
192                    thatIter = ((BasicArray)thatList).values.iterator();
193                } else {
194                    thatIter = that.iterator();
195                }
196            }
197            assert thatIter != null;
198            while (thisIter.hasNext() && thatIter.hasNext()) {
199                Object thisValue = thisIter.next();
200                Object thatValue = thatIter.next();
201                if (!BsonUtils.valuesAreEqual(thisValue, thatValue)) {
202                    return false;
203                }
204            }
205            return !thisIter.hasNext() && !thatIter.hasNext();
206        }
207        if (obj.getClass().isArray()) {
208            if (this.size() != java.lang.reflect.Array.getLength(obj)) {
209                return false;
210            }
211            Iterator<?> thisIter = values.iterator();
212            int index = 0;
213            while (thisIter.hasNext()) {
214                Object thisValue = thisIter.next();
215                Object thatValue = java.lang.reflect.Array.get(obj, index++);
216                if (!BsonUtils.valuesAreEqual(thisValue, thatValue)) {
217                    return false;
218                }
219            }
220            return true;
221        }
222        return false;
223    }
224
225    @Override
226    public String toString() {
227        return Json.write(this);
228    }
229
230    @Override
231    public int indexOf( Object o ) {
232        return values.indexOf(o);
233    }
234
235    @Override
236    public boolean isEmpty() {
237        return values.isEmpty();
238    }
239
240    @Override
241    public int lastIndexOf( Object o ) {
242        return values.lastIndexOf(o);
243    }
244
245    @Override
246    public List<Object> subList( int fromIndex,
247                                 int toIndex ) {
248        return new BasicArray(values.subList(fromIndex, toIndex));
249    }
250
251    @Override
252    public Object[] toArray() {
253        return values.toArray();
254    }
255
256    @Override
257    public <T> T[] toArray( T[] a ) {
258        return values.toArray(a);
259    }
260
261    @Override
262    public Iterator<Object> iterator() {
263        final Iterator<Object> delegate = values.iterator();
264        return new Iterator<Object>() {
265            @Override
266            public boolean hasNext() {
267                return delegate.hasNext();
268            }
269
270            @Override
271            public Object next() {
272                return delegate.next();
273            }
274
275            @Override
276            public void remove() {
277                throw new UnsupportedOperationException();
278            }
279        };
280    }
281
282    @Override
283    public Iterable<Entry> getEntries() {
284        return () -> new Iterator<Entry>() {
285            @SuppressWarnings( "synthetic-access" )
286            private final Iterator<Object> valueIter = BasicArray.this.values.iterator();
287            private int index = 0;
288
289            @Override
290            public boolean hasNext() {
291                return valueIter.hasNext();
292            }
293
294            @Override
295            public Entry next() {
296                Object value = valueIter.next();
297                return new BasicEntry(index++, value);
298            }
299
300            @Override
301            public void remove() {
302                throw new UnsupportedOperationException();
303            }
304        };
305    }
306
307    @Override
308    public Boolean getBoolean( String name ) {
309        Object value = get(name);
310        return (value instanceof Boolean) ? (Boolean)value : null;
311    }
312
313    @Override
314    public boolean getBoolean( String name,
315                               boolean defaultValue ) {
316        Object value = get(name);
317        return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : defaultValue;
318    }
319
320    @Override
321    public Integer getInteger( String name ) {
322        Object value = get(name);
323        return (value instanceof Integer) ? (Integer)value : null;
324    }
325
326    @Override
327    public int getInteger( String name,
328                           int defaultValue ) {
329        Object value = get(name);
330        return (value instanceof Integer) ? ((Integer)value).intValue() : defaultValue;
331    }
332
333    @Override
334    public Long getLong( String name ) {
335        Object value = get(name);
336        if (value instanceof Long) return (Long)value;
337        if (value instanceof Integer) return new Long(((Integer)value).longValue());
338        return null;
339    }
340
341    @Override
342    public long getLong( String name,
343                         long defaultValue ) {
344        Object value = get(name);
345        if (value instanceof Long) return ((Long)value).longValue();
346        if (value instanceof Integer) return ((Integer)value).longValue();
347        return defaultValue;
348    }
349
350    @Override
351    public Double getDouble( String name ) {
352        Object value = get(name);
353        return (value instanceof Double) ? (Double)value : null;
354    }
355
356    @Override
357    public double getDouble( String name,
358                             double defaultValue ) {
359        Object value = get(name);
360        return (value instanceof Double) ? ((Double)value).doubleValue() : defaultValue;
361    }
362
363    @Override
364    public Number getNumber( String name ) {
365        Object value = get(name);
366        return (value instanceof Number) ? (Number)value : null;
367    }
368
369    @Override
370    public Number getNumber( String name,
371                             Number defaultValue ) {
372        Object value = get(name);
373        return (value instanceof Number) ? (Number)value : defaultValue;
374    }
375
376    @Override
377    public String getString( String name ) {
378        return getString(name, null);
379    }
380
381    @Override
382    public String getString( String name,
383                             String defaultValue ) {
384        Object value = get(name);
385        if (value != null) {
386            if (value instanceof String) {
387                return (String)value;
388            }
389            if (value instanceof Symbol) {
390                return ((Symbol)value).getSymbol();
391            }
392        }
393        return defaultValue;
394    }
395
396    @Override
397    public List<?> getArray( String name ) {
398        Object value = get(name);
399        return (value instanceof List) ? (List<?>)value : null;
400    }
401
402    @Override
403    public Document getDocument( String name ) {
404        Object value = get(name);
405        return (value instanceof Document) ? (Document)value : null;
406    }
407
408    @Override
409    public boolean isNull( String name ) {
410        return get(name) instanceof Null;
411    }
412
413    @Override
414    public boolean isNullOrMissing( String name ) {
415        return Null.matches(get(name));
416    }
417
418    @Override
419    public MaxKey getMaxKey( String name ) {
420        Object value = get(name);
421        return (value instanceof MaxKey) ? (MaxKey)value : null;
422    }
423
424    @Override
425    public MinKey getMinKey( String name ) {
426        Object value = get(name);
427        return (value instanceof MinKey) ? (MinKey)value : null;
428    }
429
430    @Override
431    public Code getCode( String name ) {
432        Object value = get(name);
433        return (value instanceof Code) ? (Code)value : null;
434    }
435
436    @Override
437    public CodeWithScope getCodeWithScope( String name ) {
438        Object value = get(name);
439        return (value instanceof CodeWithScope) ? (CodeWithScope)value : null;
440    }
441
442    @Override
443    public ObjectId getObjectId( String name ) {
444        Object value = get(name);
445        return (value instanceof ObjectId) ? (ObjectId)value : null;
446    }
447
448    @Override
449    public Binary getBinary( String name ) {
450        Object value = get(name);
451        return (value instanceof Binary) ? (Binary)value : null;
452    }
453    
454    @Override
455    public Date getDate(String name) {
456        Object value = get(name);
457        return (value instanceof Date) ? (Date) value : null;
458    }
459    
460    @Override
461    public Symbol getSymbol( String name ) {
462        Object value = get(name);
463        if (value != null) {
464            if (value instanceof Symbol) {
465                return (Symbol)value;
466            }
467            if (value instanceof String) {
468                return new Symbol((String)value);
469            }
470        }
471        return null;
472    }
473
474    @Override
475    public Pattern getPattern( String name ) {
476        Object value = get(name);
477        return (value instanceof Pattern) ? (Pattern)value : null;
478    }
479
480    @Override
481    public UUID getUuid( String name ) {
482        return getUuid(name, null);
483    }
484
485    @Override
486    public UUID getUuid( String name,
487                         UUID defaultValue ) {
488        Object value = get(name);
489        if (value != null) {
490            if (value instanceof UUID) {
491                return (UUID)value;
492            }
493            if (value instanceof String) {
494                try {
495                    return UUID.fromString((String)value);
496                } catch (IllegalArgumentException e) {
497                    // do nothing ...
498                }
499            }
500        }
501        return defaultValue;
502    }
503
504    @Override
505    public int getType( String name ) {
506        return Bson.getTypeForValue(get(name));
507    }
508
509    @Override
510    public ListIterator<Object> listIterator() {
511        return new UnmodifiableListIterator(values.listIterator());
512    }
513
514    @Override
515    public ListIterator<Object> listIterator( int index ) {
516        return new UnmodifiableListIterator(values.listIterator(index));
517    }
518
519    protected static final class UnmodifiableListIterator implements ListIterator<Object> {
520        private final ListIterator<Object> delegate;
521
522        protected UnmodifiableListIterator( ListIterator<Object> delegate ) {
523            this.delegate = delegate;
524        }
525
526        @Override
527        public boolean hasNext() {
528            return delegate.hasNext();
529        }
530
531        @Override
532        public Object next() {
533            return delegate.next();
534        }
535
536        @Override
537        public void remove() {
538            throw new UnsupportedOperationException();
539        }
540
541        @Override
542        public void add( Object e ) {
543            throw new UnsupportedOperationException();
544        }
545
546        @Override
547        public boolean hasPrevious() {
548            return delegate.hasPrevious();
549        }
550
551        @Override
552        public int nextIndex() {
553            return delegate.nextIndex();
554        }
555
556        @Override
557        public Object previous() {
558            return delegate.previous();
559        }
560
561        @Override
562        public int previousIndex() {
563            return delegate.previousIndex();
564        }
565
566        @Override
567        public void set( Object e ) {
568            throw new UnsupportedOperationException();
569        }
570    }
571
572    @Override
573    public void add( int index,
574                     Object element ) {
575        throw new UnsupportedOperationException();
576    }
577
578    @Override
579    public boolean add( Object e ) {
580        throw new UnsupportedOperationException();
581    }
582
583    @Override
584    public boolean addAll( Collection<?> c ) {
585        throw new UnsupportedOperationException();
586    }
587
588    @Override
589    public boolean addAll( int index,
590                           Collection<?> c ) {
591        throw new UnsupportedOperationException();
592    }
593
594    @Override
595    public void clear() {
596        throw new UnsupportedOperationException();
597    }
598
599    @Override
600    public Object remove( int index ) {
601        throw new UnsupportedOperationException();
602    }
603
604    @Override
605    public boolean remove( Object o ) {
606        throw new UnsupportedOperationException();
607    }
608
609    @Override
610    public boolean removeAll( Collection<?> c ) {
611        throw new UnsupportedOperationException();
612    }
613
614    @Override
615    public boolean retainAll( Collection<?> c ) {
616        throw new UnsupportedOperationException();
617    }
618
619    @Override
620    public Object set( int index,
621                       Object element ) {
622        throw new UnsupportedOperationException();
623    }
624
625    protected final int indexFrom( String name ) {
626        return Integer.parseInt(name);
627    }
628
629    protected final boolean isValidIndex( int index ) {
630        return index >= 0 && index < size();
631    }
632
633    // ---------------------------------------------------------------------------------------------------------
634    // Mutation methods, for use only by the editor framework
635    // ---------------------------------------------------------------------------------------------------------
636
637    protected Object unwrap( Object value ) {
638        if (value instanceof DocumentEditor) {
639            return unwrap(((DocumentEditor)value).unwrap());
640        }
641        if (value instanceof ArrayEditor) {
642            return unwrap(((ArrayEditor)value).unwrap());
643        }
644        return value;
645    }
646
647    @Override
648    public boolean addValueIfAbsent( Object value ) {
649        value = unwrap(value);
650        return !this.values.contains(value) ? this.values.add(value) : false;
651    }
652
653    @Override
654    public int addValue( Object value ) {
655        value = unwrap(value);
656        int index = this.values.size();
657        this.values.add(index, value);
658        return index;
659    }
660
661    @Override
662    public void addValue( int index,
663                          Object value ) {
664        value = unwrap(value);
665        this.values.add(index, value);
666    }
667
668    @Override
669    public Object setValue( int index,
670                            Object value ) {
671        value = unwrap(value);
672        return this.values.set(index, value);
673    }
674
675    @Override
676    public boolean removeValue( Object value ) {
677        value = unwrap(value);
678        return this.values.remove(value);
679    }
680
681    @Override
682    public Object removeValue( int index ) {
683        return values.remove(index);
684    }
685
686    @Override
687    public boolean addAllValues( Collection<?> values ) {
688        if (values == null || values.isEmpty()) return false;
689        this.values.addAll(values.stream().map(this::unwrap).collect(Collectors.toList()));
690        return true;
691    }
692
693    @Override
694    public boolean addAllValues( int index,
695                                 Collection<?> values ) {
696        if (values == null || values.isEmpty()) return false;
697        for (Object value : values) {
698            this.values.add(index, unwrap(value));
699        }
700        return true;
701    }
702
703    @Override
704    public List<Entry> removeAllValues( Collection<?> valuesToBeRemoved ) {
705        return removeValues(valuesToBeRemoved, true);
706    }
707
708    @Override
709    public List<Entry> retainAllValues( Collection<?> valuesToBeRetained ) {
710        return removeValues(valuesToBeRetained, false);
711    }
712
713    /**
714     * Remove some of the values in this array.
715     * 
716     * @param values the values to be compared to this array's values
717     * @param ifMatch true if this method should retain all values that match the supplied values, or false if this method should
718     *        remove all values that match the supplied values
719     * @return the entries that were removed; never null
720     */
721    private List<Entry> removeValues( Collection<?> values,
722                                      boolean ifMatch ) {
723        LinkedList<Entry> results = null;
724
725        // Record the list of entries that are removed, but start at the end of the values (so the indexes are correct)
726        ListIterator<?> iter = this.values.listIterator(size());
727        while (iter.hasPrevious()) {
728            int index = iter.previousIndex();
729            Object value = iter.previous();
730            if (ifMatch == values.contains(value)) {
731                iter.remove();
732                if (results == null) {
733                    results = new LinkedList<>();
734                }
735                results.addFirst(new BasicEntry(index, value));
736            }
737        }
738
739        return results != null ? results : Collections.<Entry>emptyList();
740    }
741
742    @Immutable
743    public static final class BasicEntry implements Entry {
744        private final int index;
745        private final Object value;
746
747        public BasicEntry( int index,
748                           Object value ) {
749            this.index = index;
750            this.value = value;
751        }
752
753        @Override
754        public int getIndex() {
755            return index;
756        }
757
758        @Override
759        public Object getValue() {
760            return value;
761        }
762
763        @Override
764        public int compareTo( Entry o ) {
765            return o == this ? 0 : o == null ? 1 : o.getIndex() - this.getIndex();
766        }
767    }
768
769    @Override
770    public Object remove( String name ) {
771        try {
772            int index = indexFrom(name);
773            return isValidIndex(index) ? values.remove(index) : null;
774        } catch (NumberFormatException e) {
775            // Must be a value ...
776            return removeValue(name);
777        }
778    }
779
780    @Override
781    public void removeAll() {
782        values.clear();
783    }
784
785    @Override
786    public Object put( String name,
787                       Object value ) {
788        return put(indexFrom(name), value);
789    }
790
791    protected final Object put( int index,
792                                Object value ) {
793        final int size = size();
794        if (index == size) {
795            values.add(unwrap(value));
796            return value;
797        }
798        return values.set(index, unwrap(value)); // may throw IndexOutOfBoundsException
799    }
800
801    @Override
802    public void putAll( Document object ) {
803        if (object instanceof BasicArray) {
804            BasicArray that = (BasicArray)object;
805            addAll(that.values);
806        }
807    }
808
809    @Override
810    public void putAll( Map<? extends String, ?> map ) {
811        // Attempt to convert all of the keys to integers ...
812        List<IndexEntry> sortableEntries = new ArrayList<>(map.size());
813        for (Map.Entry<? extends String, ?> entry : map.entrySet()) {
814            int index = indexFrom(entry.getKey());
815            sortableEntries.add(new IndexEntry(index, entry.getValue()));
816        }
817        Collections.sort(sortableEntries);
818
819        // Now add them in increasing order ...
820        for (IndexEntry entry : sortableEntries) {
821            put(entry.index, unwrap(entry.value));
822        }
823    }
824
825    @Override
826    public MutableArray clone() {
827        BasicArray clone = new BasicArray();
828        for (Object value : this) {
829            value = unwrap(value);
830            if (value instanceof Array) {
831                value = ((Array)value).clone();
832            } else if (value instanceof Document) {
833                value = ((Document)value).clone();
834            }// every other kind of value is immutable
835            clone.addValue(value);
836        }
837        return clone;
838    }
839
840    @Override
841    public Array with( Map<String, Object> changedFields ) {
842        BasicArray clone = new BasicArray();
843        for (Field field : this.fields()) {
844            String name = field.getName();
845            Object newValue = unwrap(changedFields.get(name));
846            if (newValue != null) {
847                clone.put(name, newValue);
848            } else {
849                Object oldValue = field.getValue();
850                clone.put(name, oldValue);
851            }
852        }
853        return clone;
854    }
855
856    @Override
857    public Document with( String fieldName,
858                          Object newValue ) {
859        newValue = unwrap(newValue);
860        BasicArray clone = new BasicArray();
861        for (Field field : this.fields()) {
862            String name = field.getName();
863            if (name.equals(fieldName)) {
864                clone.put(name, newValue);
865            } else {
866                Object oldValue = field.getValue();
867                clone.put(name, oldValue);
868            }
869        }
870        return clone;
871    }
872
873    @Override
874    public Array with( ValueTransformer transformer ) {
875        boolean transformed = false;
876        BasicArray clone = new BasicArray();
877        for (Field field : this.fields()) {
878            String name = field.getName();
879            Object oldValue = field.getValue();
880            Object newValue = null;
881            if (oldValue instanceof Document) {
882                newValue = ((Document)oldValue).with(transformer);
883            } else {
884                newValue = transformer.transform(name, oldValue);
885            }
886            if (newValue != oldValue) transformed = true;
887            clone.put(name, unwrap(newValue));
888        }
889        return transformed ? clone : this;
890    }
891
892    @Override
893    public Array withVariablesReplaced( Properties properties ) {
894        return with(new DocumentTransformer.PropertiesTransformer(properties));
895    }
896
897    @Override
898    public Array withVariablesReplacedWithSystemProperties() {
899        return with(new DocumentTransformer.SystemPropertiesTransformer());
900    }
901
902    @Immutable
903    protected static final class IndexEntry implements Comparable<IndexEntry> {
904        protected final int index;
905        protected final Object value;
906
907        protected IndexEntry( int index,
908                              Object value ) {
909            this.index = index;
910            this.value = value;
911        }
912
913        @Override
914        public int compareTo( IndexEntry that ) {
915            return this.index - that.index;
916        }
917
918        @Override
919        public int hashCode() {
920            return index;
921        }
922
923        @Override
924        public boolean equals( Object obj ) {
925            if (obj == this) return true;
926            if (obj instanceof IndexEntry) {
927                IndexEntry that = (IndexEntry)obj;
928                if (this.index != that.index) return false;
929                if (this.value == null) return that.value == null;
930                return this.value.equals(that.value);
931            }
932            return false;
933        }
934
935        @Override
936        public String toString() {
937            return "[" + index + ',' + value + ']';
938        }
939    }
940
941}