/** * two nodes, my is unmodified, their is updated and has a higher version => their version is * going to be the merged version */ @Test public void nodeSimple_locallyUnmodifiedNoConflict() { Node n = new Node(new LatLon(0, 0)); n.setOsmId(1, 1); n.setModified(false); n.put("key1", "value1"); my.addPrimitive(n); Node n1 = new Node(new LatLon(0, 0)); n1.setOsmId(1, 2); n1.setModified(false); n1.put("key1", "value1-new"); n1.put("key2", "value2"); their.addPrimitive(n1); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Node n2 = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertTrue(visitor.getConflicts().isEmpty()); assertSame(n, n2); // make sure the merged node is still the original node assertSame(n2.getDataSet(), my); assertEquals(1, n2.getId()); assertEquals(2, n2.getVersion()); assertFalse(n2.isModified()); assertEquals("value1-new", n2.get("key1")); assertEquals("value2", n2.get("key2")); // the merge target should not be modified assertFalse(n2.isModified()); }
/** * Node with same id, my is modified, their has a higher version => results in a conflict * * <p>Use case: node which is modified locally and updated by another mapper on the server */ @Test public void nodeSimple_TagConflict() { Node n = new Node(new LatLon(0, 0)); n.setOsmId(1, 1); n.setModified(true); n.put("key1", "value1"); n.put("key2", "value2"); my.addPrimitive(n); Node n1 = new Node(new LatLon(0, 0)); n1.setOsmId(1, 2); n1.setModified(false); n1.put("key1", "value1-new"); their.addPrimitive(n1); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Node n2 = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertEquals(1, visitor.getConflicts().size()); assertSame(n, n2); assertNotSame(n1, n2); assertSame(n1.getDataSet(), their); }
// no concurrent read/write is assumed public V put(double key, V value) { if (value == null) throw new NullPointerException(); // not incrementing b's refcnt, so need not release it. Node b = skipListMap.findGrandPredecessor(key); if (b == null) b = head; Node n = b.next; if (n == null) { n = Node.alloc(); n.put(key, value, this); n.next = null; b.next = n; return null; } Node f = n.next; if (f == null) { // put (key,value) into node n Object val = n.put(key, value, this); return (V) val; } else { assert f.len() > 0 && f.first() >= key; if (f.first() == key) { Object val = f.put(key, value, this); return (V) val; } else { Object val = n.put(key, value, this); return (V) val; } } }
/** * two identical nodes, even in id and version. No confict expected. * * <p>Can happen if data is loaded in two layers and then merged from one layer on the other. */ @Test public void nodeSimple_IdenticalNoConflict() { Node n = new Node(new LatLon(0, 0)); n.setOsmId(1, 1); n.setModified(false); n.put("key1", "value1"); my.addPrimitive(n); Node n1 = new Node(new LatLon(0, 0)); n1.setOsmId(1, 1); n1.setModified(false); n1.put("key1", "value1"); their.addPrimitive(n1); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Node n2 = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertTrue(visitor.getConflicts().isEmpty()); assertNotSame(n1, n2); // make sure we have a clone assertEquals(1, n2.getId()); assertEquals(1, n2.getVersion()); assertFalse(n2.isModified()); assertEquals("value1", n2.get("key1")); // merge target not modified after merging assertFalse(n2.isModified()); }
/** * Test hasEqualSemanticAttributes on two nodes whose identical tags are added in different * orders. */ public void testHasEqualSemanticAttributes() { Node n1 = new Node(1); n1.setCoor(new LatLon(0, 0)); n1.put("key.1", "value.1"); n1.put("key.2", "value.2"); Node n2 = new Node(1); n2.setCoor(new LatLon(0, 0)); n2.put("key.2", "value.2"); n2.put("key.1", "value.1"); assertTrue(n1.hasEqualSemanticAttributes(n2)); }
@SuppressWarnings("unchecked") @Override public <E extends Entry<K, V>> E put(E entry) { if (entry.getKey() == null) { return updateNullEntry(entry); } if (entry.getValue() == null) { return remove(entry.getKey()); } Object result = root.put(conf, entry); if (result == null) { this.size++; return null; } if (result instanceof Split) { Split<K, V> split = (Split<K, V>) result; this.root = new InnerNode<K, V>(conf, root, split.getKey(), split.getGreater()); this.size++; return null; } else { return (E) result; } }
/** Remove all tags from a node */ public void testRemoveAll() { Node n = new Node(); n.put("key.1", "value.1"); n.put("key.2", "value.2"); n.removeAll(); assertTrue(n.getKeys().size() == 0); }
/** * their way has a higher version and different nodes. My way is modified. * * <p>=> merge onto my way not possible, create a conflict */ @Test public void waySimple_DifferentNodesAndMyIsModified() { // -- the target dataset Node n1 = new Node(new LatLon(0, 0)); n1.setOsmId(1, 1); my.addPrimitive(n1); Node n2 = new Node(new LatLon(1, 1)); n2.setOsmId(2, 1); my.addPrimitive(n2); Way myWay = new Way(); myWay.setOsmId(3, 1); myWay.addNode(n1); myWay.addNode(n2); myWay.setModified(true); myWay.put("key1", "value1"); my.addPrimitive(myWay); // -- the source dataset Node n3 = new Node(new LatLon(0, 0)); n3.setOsmId(1, 1); their.addPrimitive(n3); Node n5 = new Node(new LatLon(1, 1)); n5.setOsmId(4, 1); their.addPrimitive(n5); Node n4 = new Node(new LatLon(2, 2)); n4.setOsmId(2, 1); n4.put("key1", "value1"); their.addPrimitive(n4); Way theirWay = new Way(); theirWay.setOsmId(3, 2); theirWay.addNode(n3); theirWay.addNode(n5); // insert a node theirWay.addNode(n4); // this one is updated their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Way merged = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertEquals(1, visitor.getConflicts().size()); assertEquals(3, merged.getId()); assertEquals(1, merged.getVersion()); assertEquals(2, merged.getNodesCount()); assertEquals(1, merged.getNode(0).getId()); assertEquals(2, merged.getNode(1).getId()); assertEquals("value1", merged.get("key1")); }
/** Add a tag to an empty node and test the query and get methods. */ public void testPut() { Node n = new Node(); n.put("akey", "avalue"); assertTrue(n.get("akey").equals("avalue")); assertTrue(n.getKeys().size() == 1); assertTrue(n.keySet().size() == 1); assertTrue(n.keySet().contains("akey")); }
/** Add two tags to an empty node and test the query and get methods. */ public void testPut2() { Node n = new Node(); n.put("key.1", "value.1"); n.put("key.2", "value.2"); assertTrue(n.get("key.1").equals("value.1")); assertTrue(n.get("key.2").equals("value.2")); assertTrue(n.getKeys().size() == 2); assertTrue(n.hasKeys()); assertTrue(n.hasKey("key.1")); assertTrue(n.hasKey("key.2")); assertTrue(!n.hasKey("nosuchkey")); }
private void addChild(Node<K, V> node, Node<K, V> child) { if (child.getType() == NodeType.LEAF) { LeafNode leaf = (LeafNode) child; for (int i = 0; i < leaf.size(); i++) { node.put((K) leaf.getKeys().get(i), (V) leaf.getValues().get(i)); } } else if (child.getType() == NodeType.GUIDE) { GuideNode guide = (GuideNode) child; for (int i = 0; i < guide.size(); i++) { addChild(node, (Node<K, V>) guide.getKids().get(i)); } } }
// concurrent read/write access is allowed boolean appendNewAtomic(double key, Object value, ConcurrentDoubleOrderedListMap orderedMap) { synchronized (this) { if (isMarked()) return false; if (next != null) return false; Node n = Node.alloc(); n.put(key, value, orderedMap); // assert n.len()==1; n.next = null; boolean success = casNext(null, n); assert success; return true; } }
/** * their node has no assigned id (id == 0) and is semantically equal to one of my nodes with id == * 0 * * <p>=> merge it onto my node. */ @Test public void nodeSimple_NoIdSemanticallyEqual() { Calendar cal = GregorianCalendar.getInstance(); User myUser = User.createOsmUser(1111, "my"); User theirUser = User.createOsmUser(222, "their"); Node n = new Node(); n.setCoor(new LatLon(0, 0)); n.put("key1", "value1"); n.setUser(myUser); n.setTimestamp(cal.getTime()); my.addPrimitive(n); Node n1 = new Node(); n1.setCoor(new LatLon(0, 0)); n1.put("key1", "value1"); cal.add(Calendar.HOUR, 1); Date timestamp = cal.getTime(); n1.setTimestamp(timestamp); n1.setUser(theirUser); their.addPrimitive(n1); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Node n2 = my.getNodes().iterator().next(); assertEquals(0, visitor.getConflicts().size()); assertEquals("value1", n2.get("key1")); assertEquals(n1.getRawTimestamp(), n2.getRawTimestamp()); assertEquals(theirUser, n2.getUser()); assertSame(n2, n); assertNotSame(n2, n1); assertSame(n2.getDataSet(), my); }
@Override public InsertionResult put(Key key, Value value) { int i = keys().insertionPoint(key); if (i < 0) { i = -i; } Node node = children.resolve(i); InsertionResult result = node.put(key, value); if (getParent().isNull()) { return new InsertionResult(this.getNodeRef(), result.didUpdate); } return new InsertionResult(getParent(), result.didUpdate); }
@Override public Object put(Comparator comparator, Object key, Object val) { Node node = findNode(comparator, key); Object oldVal = node.put(comparator, key, val); if (isSplit(oldVal)) { Node nextNode = node.next(); Object keyForNextNode = nextNode.firstKey(); if (size == capacity) { splitBranch(comparator, keyForNextNode, nextNode); } else { insertNode(comparator, keyForNextNode, nextNode); oldVal = null; } } return oldVal; }
@SuppressWarnings("unchecked") public V put(K key, V val) { if (key == null || val == null) { throw new NullPointerException("Keys and values may not be null"); } Object o = root.put(comparator, key, val); if (isSplit(o)) { Node next = root.next(); root = Branch.newInstance(root, next, nodeSize); o = null; } if (null == o) { size++; } return (V) o; }
/** Remove tags from a node with two tags and test the state of the node. */ public void testRemove() { Node n = new Node(); n.put("key.1", "value.1"); n.put("key.2", "value.2"); n.remove("nosuchkey"); // should work assertTrue(n.getKeys().size() == 2); // still 2 tags ? n.remove("key.1"); assertTrue(n.getKeys().size() == 1); assertTrue(!n.hasKey("key.1")); assertTrue(n.get("key.1") == null); assertTrue(n.hasKey("key.2")); assertTrue(n.get("key.2").equals("value.2")); n.remove("key.2"); assertTrue(n.getKeys().size() == 0); assertTrue(!n.hasKey("key.1")); assertTrue(n.get("key.1") == null); assertTrue(!n.hasKey("key.2")); assertTrue(n.get("key.2") == null); }
/** * my node is incomplete, their node is complete * * <p>=> merge it onto my node. My node becomes complete */ @Test public void nodeSimple_IncompleteNode() { Node n = new Node(1); my.addPrimitive(n); Node n1 = new Node(); n1.setCoor(new LatLon(0, 0)); n1.setOsmId(1, 1); n1.put("key1", "value1"); Date timestamp = new Date(); n1.setTimestamp(timestamp); their.addPrimitive(n1); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Node n2 = my.getNodes().iterator().next(); assertEquals(0, visitor.getConflicts().size()); assertEquals("value1", n2.get("key1")); assertEquals(n1.getRawTimestamp(), n2.getRawTimestamp()); assertFalse(n2.isIncomplete()); assertSame(n2, n); }
// no concurrent read/write access is assumed private void putReally( int pos, double key, Object value, ConcurrentDoubleOrderedListMap orderedMap) { int len = len(); if (len + 1 <= keys.length) { // inserted in the current node nodeCopy(keys, vals, pos, keys, vals, pos + 1, len - pos); keys[pos] = key; vals[pos] = value; length = len + 1; if (pos == 0) { orderedMap.skipListMap.put(keys[0], this); if (len != 0) orderedMap.skipListMap.remove(keys[1], this); } } else if (emptySlotInNextTwo()) { if (pos == len) { next.put(key, value, orderedMap); return; } next.put(keys[len - 1], vals[len - 1], orderedMap); nodeCopy(keys, vals, pos, keys, vals, pos + 1, len - pos - 1); keys[pos] = key; vals[pos] = value; if (pos == 0) { orderedMap.skipListMap.remove(keys[1], this); orderedMap.skipListMap.put(keys[0], this); } } else { // current node is full, so requires a new node Node n = Node.alloc(); double[] nkeys = n.keys; Object[] nvals = n.vals; int l1 = len / 2, l2 = len - l1; if (next == null && pos == len) { // this is the last node, simply add to the new node. nkeys[0] = key; nvals[0] = value; n.length = 1; orderedMap.skipListMap.put(nkeys[0], n); } else if (pos < l1) { // key,value is stored in the current node length = l1 + 1; n.length = l2; nodeCopy(keys, vals, l1, nkeys, nvals, 0, l2); nodeCopy(keys, vals, pos, keys, vals, pos + 1, l1 - pos); keys[pos] = key; vals[pos] = value; if (pos == 0) { orderedMap.skipListMap.remove(keys[1]); orderedMap.skipListMap.put(keys[0], this); } orderedMap.skipListMap.put(nkeys[0], n); } else { // key,value is stored in the new node length = l1; n.length = l2 + 1; int newpos = pos - l1; nodeCopy(keys, vals, l1, nkeys, nvals, 0, newpos); nkeys[newpos] = key; nvals[newpos] = value; nodeCopy(keys, vals, pos, nkeys, nvals, newpos + 1, l2 - newpos); orderedMap.skipListMap.put(nkeys[0], n); } n.next = this.next; this.next = n; } }
/** * their way has a higher version and different tags. And it has more nodes. Two of the existing * nodes are modified. * * <p>=> merge it onto my way, no conflict */ @Test public void waySimple_AdditionalNodesAndChangedNodes() { // -- my data set Node n1 = new Node(new LatLon(0, 0)); n1.setOsmId(1, 1); my.addPrimitive(n1); Node n2 = new Node(new LatLon(1, 1)); n2.setOsmId(2, 1); my.addPrimitive(n2); Way myWay = new Way(); myWay.setOsmId(3, 1); myWay.addNode(n1); myWay.addNode(n2); my.addPrimitive(myWay); // --- their data set Node n3 = new Node(new LatLon(0, 0)); n3.setOsmId(1, 1); their.addPrimitive(n3); Node n5 = new Node(new LatLon(1, 1)); n5.setOsmId(4, 1); their.addPrimitive(n5); Node n4 = new Node(new LatLon(2, 2)); n4.setOsmId(2, 2); n4.put("key1", "value1"); their.addPrimitive(n4); Way theirWay = new Way(); theirWay.setOsmId(3, 2); theirWay.addNode(n3); theirWay.addNode(n5); // insert a node theirWay.addNode(n4); // this one is updated their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); // -- tests Way merged = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertEquals(0, visitor.getConflicts().size()); assertEquals(3, merged.getId()); assertEquals(2, merged.getVersion()); assertEquals(3, merged.getNodesCount()); assertEquals(1, merged.getNode(0).getId()); assertEquals(4, merged.getNode(1).getId()); assertEquals(2, merged.getNode(2).getId()); assertEquals("value1", merged.getNode(2).get("key1")); assertSame(merged.getNode(0), n1); assertNotSame(merged.getNode(1), n5); // must be clone of the original node in their assertSame(merged.getNode(2), n2); assertFalse( merged .isModified()); // the target wasn't modified before merging, it mustn't be after // merging }