Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

MODE-1853 Add checks to ensure DocumentEditor & ArrayEditor instances…

… are stored

Added a few bits of logic to ensure that any DocumentEditor or ArrayEditor
(which wrap Document and Array objects) are not stored. There were a few
methods that might have been storing them.
  • Loading branch information...
commit c0c29f24f876c5f9af5acf606d5a3c42ee5f4a93 1 parent 0f62cf7
@rhauch rhauch authored
View
44 modeshape-jcr/src/main/java/org/modeshape/jcr/cache/document/DocumentTranslator.java
@@ -752,11 +752,11 @@ public void changeChildren( EditableDocument document,
Set<NodeKey> removals = changedChildren.getRemovals();
Map<NodeKey, Name> newNames = changedChildren.getNewNames();
while (doc != null) {
- //we need to clean up projections
+ // we need to clean up projections
if (isFederatedDocument(doc) && !removals.isEmpty()) {
Set<String> removalsStrings = new HashSet<String>();
for (NodeKey key : removals) {
- //only when we're dealing with a foreign key do we need to do this
+ // only when we're dealing with a foreign key do we need to do this
if (!key.toString().startsWith(documentStore.getLocalSourceKey())) {
removalsStrings.add(key.toString());
}
@@ -810,28 +810,19 @@ public void changeChildren( EditableDocument document,
}
// Just append the new children to the end of the last document; we can use an asynchronous process
// to adjust/optimize the number of children in each block ...
- EditableArray lastChildren = lastDoc.getArray(CHILDREN);
- if (lastChildren == null) {
- lastChildren = lastDoc.setArray(CHILDREN);
- }
+ EditableArray lastChildren = lastDoc.getOrCreateArray(CHILDREN);
for (ChildReference ref : appended) {
lastChildren.add(fromChildReference(ref));
}
if (lastDoc != document) {
// We've written to at least one other document, so update the block size ...
- EditableDocument lastDocInfo = lastDoc.getDocument(CHILDREN_INFO);
- if (lastDocInfo == null) {
- lastDocInfo = lastDoc.setDocument(CHILDREN_INFO);
- }
+ EditableDocument lastDocInfo = lastDoc.getOrCreateDocument(CHILDREN_INFO);
lastDocInfo.setNumber(BLOCK_SIZE, lastChildren.size());
}
// And update the total size and last block on the starting document ...
- EditableDocument childInfo = document.getDocument(CHILDREN_INFO);
- if (childInfo == null) {
- childInfo = document.setDocument(CHILDREN_INFO);
- }
+ EditableDocument childInfo = document.getOrCreateDocument(CHILDREN_INFO);
newTotalSize += appended.size();
childInfo.setNumber(COUNT, newTotalSize);
@@ -848,7 +839,7 @@ protected long insertChildren( EditableDocument document,
Map<NodeKey, Name> newNames ) {
List<?> children = document.getArray(CHILDREN);
if (children == null) {
- //a federated document can have an empty children array
+ // a federated document can have an empty children array
return 0;
}
EditableArray newChildren = Schematic.newArray(children.size());
@@ -1188,7 +1179,9 @@ protected Object valueToDocument( Object value,
}
if (value instanceof ExternalBinaryValue) {
ExternalBinaryValue externalBinaryValue = (ExternalBinaryValue)value;
- return Schematic.newDocument(EXTERNAL_BINARY_ID_FIELD, externalBinaryValue.getId(), SOURCE_NAME_FIELD,
+ return Schematic.newDocument(EXTERNAL_BINARY_ID_FIELD,
+ externalBinaryValue.getId(),
+ SOURCE_NAME_FIELD,
externalBinaryValue.getSourceName());
}
if (value instanceof org.modeshape.jcr.value.BinaryValue) {
@@ -1708,12 +1701,12 @@ public boolean isLocked( EditableDocument doc ) {
return hasProperty(doc, JcrLexicon.LOCK_OWNER) || hasProperty(doc, JcrLexicon.LOCK_IS_DEEP);
}
- protected boolean isFederatedDocument(Document document) {
+ protected boolean isFederatedDocument( Document document ) {
return document.containsField(FEDERATED_SEGMENTS);
}
protected void removeFederatedSegments( EditableDocument federatedDocument,
- Set<String> externalNodeKeys) {
+ Set<String> externalNodeKeys ) {
EditableArray federatedSegments = federatedDocument.getArray(FEDERATED_SEGMENTS);
assert federatedSegments != null;
for (int i = 0; i < federatedSegments.size(); i++) {
@@ -1730,22 +1723,23 @@ protected void removeFederatedSegments( EditableDocument federatedDocument,
}
protected void removeFederatedSegments( EditableDocument federatedDocument,
- String... externalNodeKeys) {
+ String... externalNodeKeys ) {
removeFederatedSegments(federatedDocument, new HashSet<String>(Arrays.asList(externalNodeKeys)));
}
- protected boolean isQueryable(Document document) {
- //all documents are considered queryable by default
+ protected boolean isQueryable( Document document ) {
+ // all documents are considered queryable by default
return document.getBoolean(QUERYABLE_FIELD, true);
}
/**
* Marks the given document as queryable, by setting a flag.
- *
+ *
* @param document a {@link EditableDocument} instance; never null
* @param queryable a boolean which indicates whether the document should be queryable or not.
*/
- public void setQueryable(EditableDocument document, boolean queryable) {
+ public void setQueryable( EditableDocument document,
+ boolean queryable ) {
document.set(QUERYABLE_FIELD, queryable);
}
@@ -1756,7 +1750,7 @@ public void setQueryable(EditableDocument document, boolean queryable) {
* @param document a {@code non-null} {@link EditableDocument} representing the document of a local node to which the
* federated segment should be appended.
* @param documentKey a {@code non-null} {@link String} representing the key of the document. This is passed from the outside
- * as the document may not have a {@link DocumentTranslator#KEY} property (e.g. root node)
+ * as the document may not have a {@link DocumentTranslator#KEY} property (e.g. root node)
* @param sourceName a {@code non-null} string, the name of the source where {@code externalPath} will be resolved
* @param externalPath a {@code non-null} string the location in the external source which points to an external node
* @param alias an optional string representing the name under which the federated segment will be linked. In effect, this
@@ -1784,7 +1778,7 @@ protected void addFederatedSegment( EditableDocument document,
}
if (StringUtil.isBlank(projectionAlias)) {
- //we cannot create an external projection without a valid alias
+ // we cannot create an external projection without a valid alias
return;
}
View
51 modeshape-schematic/src/main/java/org/infinispan/schematic/internal/document/ArrayEditor.java
@@ -370,7 +370,6 @@ public EditableDocument setDocument( String name ) {
@Override
public EditableDocument setDocument( String name,
Document document ) {
- if (document instanceof DocumentEditor) document = ((DocumentEditor)document).unwrap();
setValue(name, document);
return editable(document, indexFrom(name));
}
@@ -534,28 +533,28 @@ protected Object doSetValue( String name,
protected Object doSetValue( int index,
Object value ) {
- value = unwrap(value);
+ value = Utility.unwrap(value);
return array.setValue(index, value);
}
protected int doAddValue( Object value ) {
- value = unwrap(value);
+ value = Utility.unwrap(value);
return array.addValue(value);
}
protected void doAddValue( int index,
Object value ) {
- value = unwrap(value);
+ value = Utility.unwrap(value);
array.addValue(index, value);
}
protected boolean doAddValueIfAbsent( Object value ) {
- value = unwrap(value);
+ value = Utility.unwrap(value);
return array.addValueIfAbsent(value);
}
protected boolean doRemoveValue( Object value ) {
- value = unwrap(value);
+ value = Utility.unwrap(value);
return array.removeValue(value);
}
@@ -564,20 +563,28 @@ protected Object doRemoveValue( int index ) {
}
protected boolean doAddAll( Collection<? extends Object> c ) {
- return array.addAllValues(c);
+ if (c == null || c.isEmpty()) return false;
+ for (Object value : c) {
+ doAddValue(value);
+ }
+ return true;
}
protected boolean doAddAll( int index,
Collection<? extends Object> c ) {
- return array.addAllValues(index, c);
+ if (c == null || c.isEmpty()) return false;
+ for (Object value : c) {
+ doAddValue(value);
+ }
+ return true;
}
protected List<Entry> doRemoveAll( Collection<?> c ) {
- return array.removeAllValues(c);
+ return array.removeAllValues(Utility.unwrapValues(c));
}
protected List<Entry> doRetainAll( Collection<?> c ) {
- return array.retainAllValues(c);
+ return array.retainAllValues(Utility.unwrapValues(c));
}
protected void doClear() {
@@ -625,30 +632,6 @@ protected EditableArray createEditableSublist( MutableArray array,
return new ArrayEditor(array, factory);
}
- public static Array unwrap( Array array ) {
- if (array instanceof ArrayEditor) {
- return unwrap(((ArrayEditor)array).unwrap());
- }
- return array;
- }
-
- public static Document unwrap( Document document ) {
- if (document instanceof DocumentEditor) {
- return unwrap(((DocumentEditor)document).unwrap());
- }
- return document;
- }
-
- public static 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 isEmpty() {
return array.isEmpty();
View
20 modeshape-schematic/src/main/java/org/infinispan/schematic/internal/document/BasicArray.java
@@ -695,13 +695,21 @@ public Object removeValue( int index ) {
@Override
public boolean addAllValues( Collection<?> values ) {
- return this.values.addAll(values);
+ if (values == null || values.isEmpty()) return false;
+ for (Object value : values) {
+ this.values.add(unwrap(value));
+ }
+ return true;
}
@Override
public boolean addAllValues( int index,
Collection<?> values ) {
- return this.values.addAll(index, values);
+ if (values == null || values.isEmpty()) return false;
+ for (Object value : values) {
+ this.values.add(index, unwrap(value));
+ }
+ return true;
}
@Override
@@ -796,17 +804,17 @@ protected final Object put( int index,
Object value ) {
final int size = size();
if (index == size) {
- values.add(value);
+ values.add(unwrap(value));
return value;
}
- return values.set(index, value); // may throw IndexOutOfBoundsException
+ return values.set(index, unwrap(value)); // may throw IndexOutOfBoundsException
}
@Override
public void putAll( Document object ) {
if (object instanceof BasicArray) {
BasicArray that = (BasicArray)object;
- this.values.addAll(that.values);
+ addAll(that.values);
}
}
@@ -822,7 +830,7 @@ public void putAll( Map<? extends String, ? extends Object> map ) {
// Now add them in increasing order ...
for (IndexEntry entry : sortableEntries) {
- put(entry.index, entry.value);
+ put(entry.index, unwrap(entry.value));
}
}
View
14 modeshape-schematic/src/main/java/org/infinispan/schematic/internal/document/BasicDocument.java
@@ -365,7 +365,9 @@ public void putAll( Document object ) {
if (object != this) {
// Prevent going through BasicBsonObject.unmodifiableView if we can ...
Map<String, ?> original = object instanceof BasicDocument ? (BasicDocument)object : object.toMap();
- super.putAll(original);
+ for (Map.Entry<String, ?> entry : original.entrySet()) {
+ put(entry.getKey(), unwrap(entry.getValue()));
+ }
}
}
@@ -503,4 +505,14 @@ public Document withVariablesReplacedWithSystemProperties() {
return with(new SystemPropertiesTransformer());
}
+ 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;
+ }
+
}
View
59 modeshape-schematic/src/main/java/org/infinispan/schematic/internal/document/DocumentEditor.java
@@ -132,17 +132,17 @@ public boolean getBoolean( String name,
public Object put( String name,
Object value ) {
- return document.put(name, value);
+ return doSetValue(name, value);
}
@Override
public void putAll( Document object ) {
- document.putAll(object);
+ doSetAllValues(object);
}
@Override
public void putAll( Map<? extends String, ? extends Object> map ) {
- document.putAll(map);
+ doSetAllValues(map);
}
@Override
@@ -547,7 +547,7 @@ protected Object doSetValue( String name,
if (value == null) {
value = Null.getInstance();
} else {
- value = unwrap(value);
+ value = Utility.unwrap(value);
}
return document.put(name, value);
}
@@ -565,11 +565,36 @@ protected Object doSetValueIfAbsent( String name,
if (value == null) {
value = Null.getInstance();
} else {
- value = unwrap(value);
+ value = Utility.unwrap(value);
}
return document.put(name, value);
}
+ /**
+ * The method that does the actual setting for all of the {@link #putAll(Document)} method. This method may be overridden by
+ * subclasses when additional work needs to be performed during this operation.
+ *
+ * @param values the document containing the fields to be added
+ */
+ protected void doSetAllValues( Document values ) {
+ if (values != null) {
+ values = Utility.unwrap(values);
+ document.putAll(values);
+ }
+ }
+
+ /**
+ * The method that does the actual setting for all of the {@link #putAll(Map)} method. This method may be overridden by
+ * subclasses when additional work needs to be performed during this operation.
+ *
+ * @param values the map containing the fields to be added
+ */
+ protected void doSetAllValues( Map<? extends String, ? extends Object> values ) {
+ if (values != null) {
+ document.putAll(Utility.unwrapValues(values));
+ }
+ }
+
protected EditableDocument editable( Document doc,
String fieldName ) {
if (doc == null) return null;
@@ -600,30 +625,6 @@ protected EditableArray createEditableArray( MutableArray array,
return new ArrayEditor(array, factory);
}
- public static Array unwrap( Array array ) {
- if (array instanceof ArrayEditor) {
- return unwrap(((ArrayEditor)array).unwrap());
- }
- return array;
- }
-
- public static Document unwrap( Document document ) {
- if (document instanceof DocumentEditor) {
- return unwrap(((DocumentEditor)document).unwrap());
- }
- return document;
- }
-
- public static 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 String toString() {
return document.toString();
View
66 modeshape-schematic/src/main/java/org/infinispan/schematic/internal/document/ObservableArrayEditor.java
@@ -22,12 +22,7 @@
package org.infinispan.schematic.internal.document;
import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
-import java.util.ListIterator;
-import java.util.Set;
import org.infinispan.schematic.document.EditableArray;
import org.infinispan.schematic.document.EditableDocument;
import org.infinispan.schematic.document.Path;
@@ -57,49 +52,6 @@ public ObservableArrayEditor( MutableArray array,
this.observer = observer;
}
- @SuppressWarnings( "unchecked" )
- protected Collection<? extends Object> unwrapValues( Collection<?> c ) {
- if (c == null || c.isEmpty()) return c;
- if (c instanceof Set<?>) {
- Set<Object> replaced = null;
- Set<Object> result = (Set<Object>)c;
- Iterator<?> iter = c.iterator();
- while (iter.hasNext()) {
- Object orig = iter.next();
- Object unwrapped = unwrap(orig);
- if (orig != unwrapped) {
- iter.remove();
- if (replaced == null) replaced = new HashSet<Object>();
- replaced.add(unwrapped);
- }
- }
- if (replaced != null) {
- result.addAll(replaced);
- }
- return result;
- }
- if (c instanceof List<?>) {
- List<Object> result = (List<Object>)c;
- ListIterator<Object> iter = result.listIterator();
- while (iter.hasNext()) {
- Object orig = iter.next();
- Object unwrapped = unwrap(orig);
- if (orig != unwrapped) {
- iter.set(unwrapped);
- }
- }
- return result;
- }
- List<Object> result = new LinkedList<Object>();
- Iterator<?> iter = result.iterator();
- while (iter.hasNext()) {
- Object orig = iter.next();
- Object unwrapped = unwrap(orig);
- result.add(unwrapped);
- }
- return result;
- }
-
@Override
protected boolean doAddAll( Collection<? extends Object> c ) {
return doAddAll(size(), c);
@@ -108,10 +60,10 @@ protected boolean doAddAll( Collection<? extends Object> c ) {
@Override
protected boolean doAddAll( int index,
Collection<? extends Object> c ) {
- c = unwrapValues(c);
+ c = Utility.unwrapValues(c);
if (super.doAddAll(index, c)) {
for (Object value : c) {
- value = unwrap(value);
+ value = Utility.unwrap(value);
observer.addOperation(new AddValueOperation(this.path, value));
}
return true;
@@ -122,14 +74,14 @@ protected boolean doAddAll( int index,
@Override
protected void doAddValue( int index,
Object value ) {
- value = unwrap(value);
+ value = Utility.unwrap(value);
super.doAddValue(index, value);
observer.addOperation(new AddValueOperation(this.path, value, index));
}
@Override
protected int doAddValue( Object value ) {
- value = unwrap(value);
+ value = Utility.unwrap(value);
int index = super.doAddValue(value);
observer.addOperation(new AddValueOperation(this.path, value));
return index;
@@ -137,7 +89,7 @@ protected int doAddValue( Object value ) {
@Override
protected boolean doAddValueIfAbsent( Object value ) {
- value = unwrap(value);
+ value = Utility.unwrap(value);
if (super.doAddValueIfAbsent(value)) {
observer.addOperation(new AddValueIfAbsentOperation(this.path, value));
return true;
@@ -153,7 +105,7 @@ protected void doClear() {
@Override
protected List<Entry> doRemoveAll( Collection<?> c ) {
- c = unwrapValues(c);
+ c = Utility.unwrapValues(c);
List<Entry> removed = super.doRemoveAll(c);
observer.addOperation(new RemoveAllValuesOperation(this.path, c));
return removed;
@@ -170,7 +122,7 @@ protected Object doRemoveValue( int index ) {
@Override
protected boolean doRemoveValue( Object value ) {
- value = unwrap(value);
+ value = Utility.unwrap(value);
if (super.doRemoveValue(value)) {
observer.addOperation(new RemoveValueOperation(this.path, value));
}
@@ -179,7 +131,7 @@ protected boolean doRemoveValue( Object value ) {
@Override
protected List<Entry> doRetainAll( Collection<?> c ) {
- c = unwrapValues(c);
+ c = Utility.unwrapValues(c);
List<Entry> removed = super.doRetainAll(c);
observer.addOperation(new RetainAllValuesOperation(this.path, c));
return removed;
@@ -188,7 +140,7 @@ protected boolean doRemoveValue( Object value ) {
@Override
protected Object doSetValue( int index,
Object value ) {
- value = unwrap(value);
+ value = Utility.unwrap(value);
Object oldValue = super.doSetValue(index, value);
observer.addOperation(new SetValueOperation(path, value, index));
return oldValue;
View
20 modeshape-schematic/src/main/java/org/infinispan/schematic/internal/document/ObservableDocumentEditor.java
@@ -21,6 +21,8 @@
*/
package org.infinispan.schematic.internal.document;
+import java.util.Map;
+import org.infinispan.schematic.document.Document;
import org.infinispan.schematic.document.EditableArray;
import org.infinispan.schematic.document.EditableDocument;
import org.infinispan.schematic.document.Path;
@@ -62,6 +64,24 @@ protected Object doSetValueIfAbsent( String name,
}
@Override
+ protected void doSetAllValues( Document values ) {
+ if (values != null && !values.isEmpty()) {
+ for (Field field : values.fields()) {
+ doSetValue(field.getName(), field.getValue());
+ }
+ }
+ }
+
+ @Override
+ protected void doSetAllValues( Map<? extends String, ? extends Object> values ) {
+ if (values != null && !values.isEmpty()) {
+ for (Map.Entry<? extends String, ? extends Object> entry : values.entrySet()) {
+ doSetValue(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ @Override
public Object remove( String name ) {
Object oldValue = super.remove(name);
observer.addOperation(new RemoveOperation(path, name, copy(oldValue)));
View
121 modeshape-schematic/src/main/java/org/infinispan/schematic/internal/document/Utility.java
@@ -0,0 +1,121 @@
+/*
+ * ModeShape (http://www.modeshape.org)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * ModeShape 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.infinispan.schematic.internal.document;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import org.infinispan.schematic.document.Array;
+import org.infinispan.schematic.document.Document;
+
+class Utility {
+
+ public static Array unwrap( Array array ) {
+ if (array instanceof ArrayEditor) {
+ return unwrap(((ArrayEditor)array).unwrap());
+ }
+ return array;
+ }
+
+ public static Document unwrap( Document document ) {
+ if (document instanceof DocumentEditor) {
+ return unwrap(((DocumentEditor)document).unwrap());
+ }
+ return document;
+ }
+
+ public static Object unwrap( Object value ) {
+ if (value instanceof DocumentEditor) {
+ return unwrap(((DocumentEditor)value).unwrap());
+ }
+ if (value instanceof ArrayEditor) {
+ return unwrap(((ArrayEditor)value).unwrap());
+ }
+ return value;
+ }
+
+ @SuppressWarnings( "unchecked" )
+ public static Map<? extends String, ? extends Object> unwrapValues( Map<? extends String, ? extends Object> map ) {
+ if (map == null || map.isEmpty()) return map;
+ Map<String, Object> newMap = (Map<String, Object>)map; // just cast
+ for (Map.Entry<? extends String, ? extends Object> entry : map.entrySet()) {
+ Object orig = entry.getValue();
+ Object unwrapped = unwrap(orig);
+ if (orig != unwrapped) {
+ String key = entry.getKey();
+ newMap.put(key, unwrapped);
+ }
+ }
+ return newMap;
+ }
+
+ @SuppressWarnings( "unchecked" )
+ public static Collection<? extends Object> unwrapValues( Collection<?> c ) {
+ if (c == null || c.isEmpty()) return c;
+ if (c instanceof Set<?>) {
+ Set<Object> replaced = null;
+ Set<Object> result = (Set<Object>)c;
+ Iterator<?> iter = c.iterator();
+ while (iter.hasNext()) {
+ Object orig = iter.next();
+ Object unwrapped = unwrap(orig);
+ if (orig != unwrapped) {
+ iter.remove();
+ if (replaced == null) replaced = new HashSet<Object>();
+ replaced.add(unwrapped);
+ }
+ }
+ if (replaced != null) {
+ result.addAll(replaced);
+ }
+ return result;
+ }
+ if (c instanceof List<?>) {
+ List<Object> result = (List<Object>)c;
+ ListIterator<Object> iter = result.listIterator();
+ while (iter.hasNext()) {
+ Object orig = iter.next();
+ Object unwrapped = unwrap(orig);
+ if (orig != unwrapped) {
+ iter.set(unwrapped);
+ }
+ }
+ return result;
+ }
+ List<Object> result = new LinkedList<Object>();
+ Iterator<?> iter = result.iterator();
+ while (iter.hasNext()) {
+ Object orig = iter.next();
+ Object unwrapped = unwrap(orig);
+ result.add(unwrapped);
+ }
+ return result;
+ }
+
+}
Please sign in to comment.
Something went wrong with that request. Please try again.