Permalink
Show file tree
Hide file tree
37 changes: 16 additions & 21 deletions
37
...i-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
XWIKI-19223: Improve xobject memory storage in XWikidocument
- Loading branch information
Showing
4 changed files
with
303 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
152 changes: 152 additions & 0 deletions
152
...orm-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/doc/BaseObjects.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| /* | ||
| * See the NOTICE file distributed with this work for additional | ||
| * information regarding copyright ownership. | ||
| * | ||
| * This is free software; you can redistribute it and/or modify it | ||
| * 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. | ||
| * | ||
| * This software 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 com.xpn.xwiki.internal.doc; | ||
|
|
||
| import java.util.AbstractList; | ||
| import java.util.Collection; | ||
| import java.util.Map; | ||
| import java.util.concurrent.ConcurrentSkipListMap; | ||
|
|
||
| import com.xpn.xwiki.objects.BaseObject; | ||
|
|
||
| /** | ||
| * Implement a list of {@link BaseObject} as seen by a document (meaning that the list index matched the object number) | ||
| * using a {@link Map} storage to avoid wasting memory with null entries. | ||
| * | ||
| * @version $Id$ | ||
| * @since 14.0RC1 | ||
| */ | ||
| // TODO: expose APIs to navigate non null objects directly instead of going through each entry and skipping explicitly | ||
| // null ones | ||
| public class BaseObjects extends AbstractList<BaseObject> | ||
| { | ||
| // Sort keys so that it's possible to navigate non null entries without going through them all | ||
| private Map<Integer, BaseObject> map = new ConcurrentSkipListMap<>(); | ||
|
|
||
| private int size; | ||
|
|
||
| /** | ||
| * Constructs an empty list. | ||
| */ | ||
| public BaseObjects() | ||
| { | ||
|
|
||
| } | ||
|
|
||
| /** | ||
| * Constructs a list containing the elements of the specified collection, in the order they are returned by the | ||
| * collection's iterator. | ||
| * | ||
| * @param collection the collection to copy | ||
| */ | ||
| public BaseObjects(Collection<BaseObject> collection) | ||
| { | ||
| collection.forEach(this::add); | ||
| } | ||
|
|
||
| @Override | ||
| public BaseObject get(int index) | ||
| { | ||
| rangeCheck(index); | ||
|
|
||
| return this.map.get(index); | ||
| } | ||
|
|
||
| @Override | ||
| public int size() | ||
| { | ||
| return this.size; | ||
| } | ||
|
|
||
| private BaseObject put(int index, BaseObject element) | ||
| { | ||
| BaseObject old; | ||
| if (element == null) { | ||
| // We don't want to keep null values in memory | ||
| old = this.map.remove(index); | ||
| } else { | ||
| // Make sure the object number is right | ||
| element.setNumber(index); | ||
|
|
||
| old = this.map.put(index, element); | ||
| } | ||
|
|
||
| // Increment size if needed | ||
| if (this.size <= index) { | ||
| this.size = index + 1; | ||
| } | ||
|
|
||
| return old; | ||
| } | ||
|
|
||
| @Override | ||
| public void add(int index, BaseObject element) | ||
| { | ||
| // Check if the index is valid | ||
| rangeCheckForAdd(index); | ||
|
|
||
| // Move right values | ||
| if (index < this.size) { | ||
| for (int i = this.size - 1; i >= index; --i) { | ||
| put(i + 1, get(i)); | ||
| } | ||
| } | ||
|
|
||
| // Insert new value | ||
| put(index, element); | ||
| } | ||
|
|
||
| @Override | ||
| public BaseObject set(int index, BaseObject element) | ||
| { | ||
| // Check if the index is valid | ||
| rangeCheck(index); | ||
|
|
||
| // Set the value and remember the old one | ||
| return put(index, element); | ||
| } | ||
|
|
||
| @Override | ||
| public BaseObject remove(int index) | ||
| { | ||
| rangeCheck(index); | ||
|
|
||
| return this.map.remove(index); | ||
| } | ||
|
|
||
| private void rangeCheck(int index) | ||
| { | ||
| if (index < 0 || index >= this.size) { | ||
| throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); | ||
| } | ||
| } | ||
|
|
||
| private void rangeCheckForAdd(int index) | ||
| { | ||
| if (index < 0 || index > this.size || index == Integer.MAX_VALUE) { | ||
| throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); | ||
| } | ||
| } | ||
|
|
||
| private String outOfBoundsMsg(int index) | ||
| { | ||
| return "Index: " + index + ", Size: " + size; | ||
| } | ||
| } |
131 changes: 131 additions & 0 deletions
131
...core/xwiki-platform-oldcore/src/test/java/com/xpn/xwiki/internal/doc/BaseObjectsTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| /* | ||
| * See the NOTICE file distributed with this work for additional | ||
| * information regarding copyright ownership. | ||
| * | ||
| * This is free software; you can redistribute it and/or modify it | ||
| * 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. | ||
| * | ||
| * This software 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 com.xpn.xwiki.internal.doc; | ||
|
|
||
| import java.util.Arrays; | ||
|
|
||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import com.xpn.xwiki.objects.BaseObject; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertNull; | ||
| import static org.junit.jupiter.api.Assertions.assertSame; | ||
| import static org.junit.jupiter.api.Assertions.assertThrows; | ||
|
|
||
| /** | ||
| * Validate {@link BaseObjects}. | ||
| * | ||
| * @version $Id$ | ||
| */ | ||
| public class BaseObjectsTest | ||
| { | ||
| private static class TestBaseObject extends BaseObject | ||
| { | ||
| @Override | ||
| public String toString() | ||
| { | ||
| return String.valueOf(getNumber()); | ||
| } | ||
| } | ||
|
|
||
| private static final BaseObject XOBJ1 = new TestBaseObject(); | ||
|
|
||
| private static final BaseObject XOBJ2 = new TestBaseObject(); | ||
|
|
||
| private static final BaseObject XOBJ3 = new TestBaseObject(); | ||
|
|
||
| private static final BaseObject XOBJ4 = new TestBaseObject(); | ||
|
|
||
| @Test | ||
| void add() | ||
| { | ||
| BaseObjects objects = new BaseObjects(); | ||
|
|
||
| assertEquals(0, objects.size()); | ||
|
|
||
| objects.add(XOBJ1); | ||
|
|
||
| assertEquals(1, objects.size()); | ||
|
|
||
| objects.add(null); | ||
|
|
||
| assertEquals(2, objects.size()); | ||
|
|
||
| objects.add(XOBJ2); | ||
|
|
||
| assertEquals(3, objects.size()); | ||
| assertSame(XOBJ1, objects.get(0)); | ||
| assertEquals(0, objects.get(0).getNumber()); | ||
| assertNull(objects.get(1)); | ||
| assertSame(XOBJ2, objects.get(2)); | ||
| assertEquals(2, objects.get(2).getNumber()); | ||
|
|
||
| objects.add(1, XOBJ3); | ||
|
|
||
| assertEquals(4, objects.size()); | ||
| assertSame(XOBJ1, objects.get(0)); | ||
| assertEquals(0, objects.get(0).getNumber()); | ||
| assertSame(XOBJ3, objects.get(1)); | ||
| assertEquals(1, objects.get(1).getNumber()); | ||
| assertNull(objects.get(2)); | ||
| assertSame(XOBJ2, objects.get(3)); | ||
| assertEquals(3, objects.get(3).getNumber()); | ||
|
|
||
| assertThrows(IndexOutOfBoundsException.class, () -> objects.add(Integer.MAX_VALUE, XOBJ4)); | ||
| } | ||
|
|
||
| @Test | ||
| void set() | ||
| { | ||
| BaseObjects objects = new BaseObjects(); | ||
|
|
||
| assertEquals(0, objects.size()); | ||
|
|
||
| assertThrows(IndexOutOfBoundsException.class, () -> objects.set(0, XOBJ1)); | ||
|
|
||
| objects.add(XOBJ1); | ||
| objects.add(XOBJ2); | ||
| objects.add(XOBJ3); | ||
|
|
||
| objects.set(1, XOBJ4); | ||
|
|
||
| assertSame(XOBJ1, objects.get(0)); | ||
| assertSame(XOBJ4, objects.get(1)); | ||
| assertSame(XOBJ3, objects.get(2)); | ||
| } | ||
|
|
||
| @Test | ||
| void remove() | ||
| { | ||
| BaseObjects objects = new BaseObjects(Arrays.asList(XOBJ1, XOBJ2, XOBJ3)); | ||
|
|
||
| assertEquals(3, objects.size()); | ||
|
|
||
| objects.remove(0); | ||
| objects.remove(2); | ||
|
|
||
| assertNull(objects.get(0)); | ||
| assertSame(XOBJ2, objects.get(1)); | ||
| assertNull(objects.get(2)); | ||
|
|
||
| assertEquals(3, objects.size()); | ||
| } | ||
| } |