merge all into single repo
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>gumtree</artifactId>
|
||||
<groupId>com.github.gumtreediff</groupId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>core</artifactId>
|
||||
<name>GumTree Core Module</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.mpkorstanje</groupId>
|
||||
<artifactId>simmetrics-core</artifactId>
|
||||
<version>3.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.trove4j</groupId>
|
||||
<artifactId>trove4j</artifactId>
|
||||
<version>3.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.actions;
|
||||
|
||||
import com.github.gumtreediff.actions.model.*;
|
||||
import com.github.gumtreediff.matchers.Mapping;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.tree.AbstractTree;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeUtils;
|
||||
import gnu.trove.map.TIntObjectMap;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class ActionGenerator {
|
||||
|
||||
private ITree origSrc;
|
||||
|
||||
private ITree newSrc;
|
||||
|
||||
private ITree origDst;
|
||||
|
||||
private MappingStore origMappings;
|
||||
|
||||
private MappingStore newMappings;
|
||||
|
||||
private Set<ITree> dstInOrder;
|
||||
|
||||
private Set<ITree> srcInOrder;
|
||||
|
||||
private int lastId;
|
||||
|
||||
private List<Action> actions;
|
||||
|
||||
private TIntObjectMap<ITree> origSrcTrees;
|
||||
|
||||
private TIntObjectMap<ITree> cpySrcTrees;
|
||||
|
||||
public ActionGenerator(ITree src, ITree dst, MappingStore mappings) {
|
||||
this.origSrc = src;
|
||||
this.newSrc = this.origSrc.deepCopy();
|
||||
this.origDst = dst;
|
||||
|
||||
origSrcTrees = new TIntObjectHashMap<>();
|
||||
for (ITree t: origSrc.getTrees())
|
||||
origSrcTrees.put(t.getId(), t);
|
||||
cpySrcTrees = new TIntObjectHashMap<>();
|
||||
for (ITree t: newSrc.getTrees())
|
||||
cpySrcTrees.put(t.getId(), t);
|
||||
|
||||
origMappings = new MappingStore();
|
||||
for (Mapping m: mappings)
|
||||
this.origMappings.link(cpySrcTrees.get(m.getFirst().getId()), m.getSecond());
|
||||
this.newMappings = origMappings.copy();
|
||||
}
|
||||
|
||||
public List<Action> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
public List<Action> generate() {
|
||||
ITree srcFakeRoot = new AbstractTree.FakeTree(newSrc);
|
||||
ITree dstFakeRoot = new AbstractTree.FakeTree(origDst);
|
||||
newSrc.setParent(srcFakeRoot);
|
||||
origDst.setParent(dstFakeRoot);
|
||||
|
||||
actions = new ArrayList<>();
|
||||
dstInOrder = new HashSet<>();
|
||||
srcInOrder = new HashSet<>();
|
||||
|
||||
lastId = newSrc.getSize() + 1;
|
||||
newMappings.link(srcFakeRoot, dstFakeRoot);
|
||||
|
||||
List<ITree> bfsDst = TreeUtils.breadthFirst(origDst);
|
||||
for (ITree x: bfsDst) {
|
||||
ITree w = null;
|
||||
ITree y = x.getParent();
|
||||
ITree z = newMappings.getSrc(y);
|
||||
|
||||
if (!newMappings.hasDst(x)) {
|
||||
int k = findPos(x);
|
||||
// Insertion case : insert new node.
|
||||
w = new AbstractTree.FakeTree();
|
||||
w.setId(newId());
|
||||
// In order to use the real nodes from the second tree, we
|
||||
// furnish x instead of w and fake that x has the newly
|
||||
// generated ID.
|
||||
Action ins = new Insert(x, origSrcTrees.get(z.getId()), k);
|
||||
actions.add(ins);
|
||||
//System.out.println(ins);
|
||||
origSrcTrees.put(w.getId(), x);
|
||||
newMappings.link(w, x);
|
||||
z.getChildren().add(k, w);
|
||||
w.setParent(z);
|
||||
} else {
|
||||
w = newMappings.getSrc(x);
|
||||
if (!x.equals(origDst)) { // TODO => x != origDst // Case of the root
|
||||
ITree v = w.getParent();
|
||||
if (!w.getLabel().equals(x.getLabel())) {
|
||||
actions.add(new Update(origSrcTrees.get(w.getId()), x));
|
||||
w.setLabel(x.getLabel());
|
||||
}
|
||||
if (!z.equals(v)) {
|
||||
int k = findPos(x);
|
||||
// Action mv = new Move(origSrcTrees.get(w.getId()), origSrcTrees.get(z.getId()), k);
|
||||
Action mv = new Move(origSrcTrees.get(w.getId()), origSrcTrees.get(z.getId()), x, k);
|
||||
actions.add(mv);
|
||||
//System.out.println(mv);
|
||||
int oldk = w.positionInParent();
|
||||
z.getChildren().add(k, w);
|
||||
w.getParent().getChildren().remove(oldk);
|
||||
w.setParent(z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//FIXME not sure why :D
|
||||
srcInOrder.add(w);
|
||||
dstInOrder.add(x);
|
||||
alignChildren(w, x);
|
||||
}
|
||||
|
||||
for (ITree w : newSrc.postOrder()) {
|
||||
if (!newMappings.hasSrc(w)) {
|
||||
actions.add(new Delete(origSrcTrees.get(w.getId())));
|
||||
//w.getParent().getChildren().remove(w);
|
||||
}
|
||||
}
|
||||
|
||||
//FIXME should ensure isomorphism.
|
||||
return actions;
|
||||
}
|
||||
|
||||
private void alignChildren(ITree w, ITree x) {
|
||||
srcInOrder.removeAll(w.getChildren());
|
||||
dstInOrder.removeAll(x.getChildren());
|
||||
|
||||
List<ITree> s1 = new ArrayList<>();
|
||||
for (ITree c: w.getChildren())
|
||||
if (newMappings.hasSrc(c))
|
||||
if (x.getChildren().contains(newMappings.getDst(c)))
|
||||
s1.add(c);
|
||||
|
||||
List<ITree> s2 = new ArrayList<>();
|
||||
for (ITree c: x.getChildren())
|
||||
if (newMappings.hasDst(c))
|
||||
if (w.getChildren().contains(newMappings.getSrc(c)))
|
||||
s2.add(c);
|
||||
|
||||
List<Mapping> lcs = lcs(s1, s2);
|
||||
|
||||
for (Mapping m : lcs) {
|
||||
srcInOrder.add(m.getFirst());
|
||||
dstInOrder.add(m.getSecond());
|
||||
}
|
||||
|
||||
for (ITree a : s1) {
|
||||
for (ITree b: s2 ) {
|
||||
if (origMappings.has(a, b)) {
|
||||
if (!lcs.contains(new Mapping(a, b))) {
|
||||
int k = findPos(b);
|
||||
// Action mv = new Move(origSrcTrees.get(a.getId()), origSrcTrees.get(w.getId()), k);
|
||||
Action mv = new Move(origSrcTrees.get(a.getId()), origSrcTrees.get(w.getId()), b, k);
|
||||
actions.add(mv);
|
||||
//System.out.println(mv);
|
||||
int oldk = a.positionInParent();
|
||||
w.getChildren().add(k, a);
|
||||
if (k < oldk ) // FIXME this is an ugly way to patch the index
|
||||
oldk ++;
|
||||
a.getParent().getChildren().remove(oldk);
|
||||
a.setParent(w);
|
||||
srcInOrder.add(a);
|
||||
dstInOrder.add(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int findPos(ITree x) {
|
||||
ITree y = x.getParent();
|
||||
List<ITree> siblings = y.getChildren();
|
||||
|
||||
for (ITree c : siblings) {
|
||||
if (dstInOrder.contains(c)) {
|
||||
if (c.equals(x)) return 0;
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
int xpos = x.positionInParent();
|
||||
ITree v = null;
|
||||
for (int i = 0; i < xpos; i++) {
|
||||
ITree c = siblings.get(i);
|
||||
if (dstInOrder.contains(c)) v = c;
|
||||
}
|
||||
|
||||
//if (v == null) throw new RuntimeException("No rightmost sibling in order");
|
||||
if (v == null) return 0;
|
||||
|
||||
ITree u = newMappings.getSrc(v);
|
||||
// siblings = u.getParent().getChildren();
|
||||
// int upos = siblings.indexOf(u);
|
||||
int upos = u.positionInParent();
|
||||
// int r = 0;
|
||||
// for (int i = 0; i <= upos; i++)
|
||||
// if (srcInOrder.contains(siblings.get(i))) r++;
|
||||
return upos + 1;
|
||||
}
|
||||
|
||||
private int newId() {
|
||||
return ++lastId;
|
||||
}
|
||||
|
||||
private List<Mapping> lcs(List<ITree> x, List<ITree> y) {
|
||||
int m = x.size();
|
||||
int n = y.size();
|
||||
List<Mapping> lcs = new ArrayList<>();
|
||||
|
||||
int[][] opt = new int[m + 1][n + 1];
|
||||
for (int i = m - 1; i >= 0; i--) {
|
||||
for (int j = n - 1; j >= 0; j--) {
|
||||
if (newMappings.getSrc(y.get(j)).equals(x.get(i))) opt[i][j] = opt[i + 1][j + 1] + 1;
|
||||
else opt[i][j] = Math.max(opt[i + 1][j], opt[i][j + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
int i = 0, j = 0;
|
||||
while (i < m && j < n) {
|
||||
if (newMappings.getSrc(y.get(j)).equals(x.get(i))) {
|
||||
lcs.add(new Mapping(x.get(i), y.get(j)));
|
||||
i++;
|
||||
j++;
|
||||
} else if (opt[i + 1][j] >= opt[i][j + 1]) i++;
|
||||
else j++;
|
||||
}
|
||||
|
||||
return lcs;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.actions;
|
||||
|
||||
import com.github.gumtreediff.actions.model.*;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ActionUtil {
|
||||
private ActionUtil() {}
|
||||
|
||||
public static TreeContext apply(TreeContext context, List<Action> actions) {
|
||||
for (Action a: actions) {
|
||||
if (a instanceof Insert) {
|
||||
Insert action = ((Insert) a);
|
||||
action.getParent().insertChild(action.getNode(), action.getPosition());
|
||||
} else if (a instanceof Update) {
|
||||
Update action = ((Update) a);
|
||||
action.getNode().setLabel(action.getValue());
|
||||
} else if (a instanceof Move) {
|
||||
Move action = ((Move) a);
|
||||
action.getNode().getParent().getChildren().remove(action.getNode());
|
||||
action.getParent().insertChild(action.getNode(), action.getPosition());
|
||||
} else if (a instanceof Delete) {
|
||||
Delete action = ((Delete) a);
|
||||
action.getNode().getParent().getChildren().remove(action.getNode());
|
||||
} else throw new RuntimeException("No such action: " + a );
|
||||
}
|
||||
return context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.actions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.github.gumtreediff.actions.model.Delete;
|
||||
import com.github.gumtreediff.actions.model.Update;
|
||||
import com.github.gumtreediff.actions.model.Action;
|
||||
import com.github.gumtreediff.actions.model.Insert;
|
||||
import com.github.gumtreediff.actions.model.Move;
|
||||
import com.github.gumtreediff.matchers.Mapping;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
|
||||
public class LeavesClassifier extends TreeClassifier {
|
||||
|
||||
public LeavesClassifier(TreeContext src, TreeContext dst, Set<Mapping> rawMappings, List<Action> actions) {
|
||||
super(src, dst, rawMappings, actions);
|
||||
}
|
||||
|
||||
public LeavesClassifier(TreeContext src, TreeContext dst, Matcher m) {
|
||||
super(src, dst, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void classify() {
|
||||
for (Action a: actions) {
|
||||
if (a instanceof Delete && isLeafAction(a)) {
|
||||
srcDelTrees.add(a.getNode());
|
||||
} else if (a instanceof Insert && isLeafAction(a)) {
|
||||
dstAddTrees.add(a.getNode());
|
||||
} else if (a instanceof Update && isLeafAction(a)) {
|
||||
srcUpdTrees.add(a.getNode());
|
||||
dstUpdTrees.add(mappings.getDst(a.getNode()));
|
||||
} else if (a instanceof Move && isLeafAction(a)) {
|
||||
srcMvTrees.add(a.getNode());
|
||||
dstMvTrees.add(mappings.getDst(a.getNode()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLeafAction(Action a) {
|
||||
for (ITree d: a.getNode().getDescendants()) {
|
||||
for (Action c: actions)
|
||||
if (a != c && d == c.getNode()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.actions;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.github.gumtreediff.actions.model.Delete;
|
||||
import com.github.gumtreediff.actions.model.Action;
|
||||
import com.github.gumtreediff.actions.model.Insert;
|
||||
import com.github.gumtreediff.actions.model.Move;
|
||||
import com.github.gumtreediff.actions.model.Update;
|
||||
import com.github.gumtreediff.matchers.Mapping;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
|
||||
public class RootAndLeavesClassifier extends TreeClassifier {
|
||||
|
||||
public RootAndLeavesClassifier(TreeContext src, TreeContext dst, Set<Mapping> rawMappings, List<Action> actions) {
|
||||
super(src, dst, rawMappings, actions);
|
||||
}
|
||||
|
||||
public RootAndLeavesClassifier(TreeContext src, TreeContext dst, Matcher m) {
|
||||
super(src, dst, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void classify() {
|
||||
for (Action a: actions) {
|
||||
if (a instanceof Insert) {
|
||||
dstAddTrees.add(a.getNode());
|
||||
} else if (a instanceof Delete) {
|
||||
srcDelTrees.add(a.getNode());
|
||||
} else if (a instanceof Update) {
|
||||
srcUpdTrees.add(a.getNode());
|
||||
dstUpdTrees.add(mappings.getDst(a.getNode()));
|
||||
} else if (a instanceof Move) {
|
||||
srcMvTrees.add(a.getNode());
|
||||
dstMvTrees.add(mappings.getDst(a.getNode()));
|
||||
}
|
||||
}
|
||||
|
||||
Set<ITree> fDstAddTrees = new HashSet<>();
|
||||
for (ITree t: dstAddTrees)
|
||||
if (!dstAddTrees.contains(t.getParent()))
|
||||
fDstAddTrees.add(t);
|
||||
dstAddTrees = fDstAddTrees;
|
||||
|
||||
Set<ITree> fSrcDelTrees = new HashSet<>();
|
||||
for (ITree t: srcDelTrees) {
|
||||
if (!srcDelTrees.contains(t.getParent()))
|
||||
fSrcDelTrees.add(t);
|
||||
}
|
||||
srcDelTrees = fSrcDelTrees;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
Set<ITree> fSrcMvTrees = new HashSet<>(); // FIXME check why it's unused
|
||||
for (ITree t: srcDelTrees) {
|
||||
if (!srcDelTrees.contains(t.getParent()))
|
||||
fSrcDelTrees.add(t);
|
||||
}
|
||||
srcDelTrees = fSrcDelTrees;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.actions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.github.gumtreediff.actions.model.Delete;
|
||||
import com.github.gumtreediff.actions.model.Move;
|
||||
import com.github.gumtreediff.actions.model.Update;
|
||||
import com.github.gumtreediff.actions.model.Action;
|
||||
import com.github.gumtreediff.actions.model.Insert;
|
||||
import com.github.gumtreediff.matchers.Mapping;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
|
||||
public class RootsClassifier extends TreeClassifier {
|
||||
|
||||
public RootsClassifier(TreeContext src, TreeContext dst, Set<Mapping> rawMappings, List<Action> script) {
|
||||
super(src, dst, rawMappings, script);
|
||||
}
|
||||
|
||||
public RootsClassifier(TreeContext src, TreeContext dst, Matcher m) {
|
||||
super(src, dst, m);
|
||||
}
|
||||
|
||||
public void classify() {
|
||||
for (Action a: actions) {
|
||||
if (a instanceof Delete) srcDelTrees.add(a.getNode());
|
||||
else if (a instanceof Insert)
|
||||
dstAddTrees.add(a.getNode());
|
||||
else if (a instanceof Update) {
|
||||
srcUpdTrees.add(a.getNode());
|
||||
dstUpdTrees.add(mappings.getDst(a.getNode()));
|
||||
} else if (a instanceof Move) {
|
||||
srcMvTrees.add(a.getNode());
|
||||
dstMvTrees.add(mappings.getDst(a.getNode()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.actions;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.github.gumtreediff.actions.model.Action;
|
||||
import com.github.gumtreediff.matchers.Mapping;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
|
||||
public abstract class TreeClassifier {
|
||||
|
||||
protected Set<ITree> srcUpdTrees;
|
||||
|
||||
protected Set<ITree> dstUpdTrees;
|
||||
|
||||
protected Set<ITree> srcMvTrees;
|
||||
|
||||
protected Set<ITree> dstMvTrees;
|
||||
|
||||
protected Set<ITree> srcDelTrees;
|
||||
|
||||
protected Set<ITree> dstAddTrees;
|
||||
|
||||
protected TreeContext src;
|
||||
|
||||
protected TreeContext dst;
|
||||
|
||||
protected MappingStore mappings;
|
||||
|
||||
protected List<Action> actions;
|
||||
|
||||
public TreeClassifier(TreeContext src, TreeContext dst, Set<Mapping> rawMappings, List<Action> actions) {
|
||||
this(src, dst, rawMappings);
|
||||
this.actions = actions;
|
||||
classify();
|
||||
}
|
||||
|
||||
public TreeClassifier(TreeContext src, TreeContext dst, Matcher m) {
|
||||
this(src, dst, m.getMappingSet());
|
||||
ActionGenerator g = new ActionGenerator(src.getRoot(), dst.getRoot(), m.getMappings());
|
||||
g.generate();
|
||||
this.actions = g.getActions();
|
||||
classify();
|
||||
}
|
||||
|
||||
private TreeClassifier(TreeContext src, TreeContext dst, Set<Mapping> rawMappings) {
|
||||
this.src = src;
|
||||
this.dst = dst;
|
||||
this.mappings = new MappingStore(rawMappings);
|
||||
this.srcDelTrees = new HashSet<>();
|
||||
this.srcMvTrees = new HashSet<>();
|
||||
this.srcUpdTrees = new HashSet<>();
|
||||
this.dstMvTrees = new HashSet<>();
|
||||
this.dstAddTrees = new HashSet<>();
|
||||
this.dstUpdTrees = new HashSet<>();
|
||||
}
|
||||
|
||||
public abstract void classify();
|
||||
|
||||
public Set<ITree> getSrcUpdTrees() {
|
||||
return srcUpdTrees;
|
||||
}
|
||||
|
||||
public Set<ITree> getDstUpdTrees() {
|
||||
return dstUpdTrees;
|
||||
}
|
||||
|
||||
public Set<ITree> getSrcMvTrees() {
|
||||
return srcMvTrees;
|
||||
}
|
||||
|
||||
public Set<ITree> getDstMvTrees() {
|
||||
return dstMvTrees;
|
||||
}
|
||||
|
||||
public Set<ITree> getSrcDelTrees() {
|
||||
return srcDelTrees;
|
||||
}
|
||||
|
||||
public Set<ITree> getDstAddTrees() {
|
||||
return dstAddTrees;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.actions.model;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public abstract class Action implements Comparable<Action>,Serializable {
|
||||
|
||||
protected ITree node;
|
||||
protected Integer position;
|
||||
protected int length = 0;
|
||||
|
||||
public Action(ITree node, int pos, int length) {
|
||||
this.node = node;
|
||||
this.length = length;
|
||||
this.position = pos;
|
||||
}
|
||||
|
||||
public ITree getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public void setNode(ITree node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public abstract String getName();
|
||||
|
||||
@Override
|
||||
public abstract String toString();
|
||||
|
||||
@Override
|
||||
public int compareTo(Action o) {
|
||||
int result = this.position.compareTo(o.position);
|
||||
if (result == 0) {
|
||||
result = this.length >= o.length ? -1 : 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.actions.model;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
public abstract class Addition extends Action {
|
||||
|
||||
protected ITree parent;
|
||||
protected int pos;//index position of the new node in the children array list of its corresponding old parent node.
|
||||
|
||||
public Addition(ITree node, ITree parent, int pos) {
|
||||
super(node, node.getPos(), node.getLength());
|
||||
this.parent = parent;
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
public ITree getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public int getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName() + " " + node.toShortString() + " @TO@ " + parent.toShortString() + " @AT@ " + position + " @LENGTH@ " + length;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.actions.model;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
public class Delete extends Action {
|
||||
|
||||
public Delete(ITree node) {
|
||||
super(node, node.getPos(), node.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "DEL";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// node.toShortString: getType()@@getLabel()
|
||||
// return getName() + " " + node.toShortString() + " / "+node.getChildrenLabels() + " at " + node.getPos();
|
||||
return getName() + " " + node.toShortString() + " @AT@ " + position + " @LENGTH@ " + length;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.actions.model;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
public class Insert extends Addition {
|
||||
|
||||
public Insert(ITree node, ITree parent, int pos) {
|
||||
super(node, parent, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "INS";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.actions.model;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
public class Move extends Addition {
|
||||
|
||||
private ITree newNode;
|
||||
|
||||
public Move(ITree node, ITree parent, int pos) {
|
||||
super(node, parent, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node, old node.
|
||||
* @param parent, parent of old node.
|
||||
* @param newNode, new node.
|
||||
* @param pos, position of the new node in the children array list of its corresponding old parent node.
|
||||
*/
|
||||
public Move(ITree node, ITree parent, ITree newNode, int pos) {
|
||||
this(node, parent, pos);
|
||||
this.newNode = newNode;
|
||||
}
|
||||
|
||||
public ITree getNewNode() {
|
||||
return this.newNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "MOV";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.actions.model;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
public class Update extends Action {
|
||||
|
||||
private String value;
|
||||
private ITree newNode;
|
||||
|
||||
public Update(ITree node, ITree newNode) {
|
||||
super(node, node.getPos(), node.getLength());
|
||||
this.value = newNode.getLabel();
|
||||
this.newNode = newNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "UPD";
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public ITree getNewNode() {
|
||||
return this.newNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// node.toShortString: getType()@@getLabel()
|
||||
return getName() + " " + node.toShortString() + " @TO@ " + value + " @AT@ " + position + " @LENGTH@ " + length;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.gen;
|
||||
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Generators extends Registry<String, TreeGenerator, Register> {
|
||||
|
||||
private static Generators registry;
|
||||
|
||||
public static final Generators getInstance() {
|
||||
if (registry == null)
|
||||
registry = new Generators();
|
||||
return registry;
|
||||
}
|
||||
|
||||
public TreeContext getTree(String file) throws UnsupportedOperationException, IOException {
|
||||
TreeGenerator p = get(file);
|
||||
if (p == null)
|
||||
throw new UnsupportedOperationException("No generator found for file: " + file);
|
||||
return p.generateFromFile(file);
|
||||
}
|
||||
|
||||
public TreeContext getTree(String generator, String file) throws UnsupportedOperationException, IOException {
|
||||
for (Entry e : entries)
|
||||
if (e.id.equals(generator))
|
||||
return e.instantiate(null).generateFromFile(file);
|
||||
throw new UnsupportedOperationException("No generator \"" + generator + "\" found.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Entry newEntry(Class<? extends TreeGenerator> clazz, Register annotation) {
|
||||
return new Entry(annotation.id(), clazz, defaultFactory(clazz), annotation.priority()) {
|
||||
final Pattern[] accept;
|
||||
|
||||
{
|
||||
String[] accept = annotation.accept();
|
||||
this.accept = new Pattern[accept.length];
|
||||
for (int i = 0; i < accept.length; i++)
|
||||
this.accept[i] = Pattern.compile(accept[i]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean handle(String key) {
|
||||
for (Pattern pattern : accept)
|
||||
if (pattern.matcher(key).find())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s: %s", Arrays.toString(accept), clazz.getCanonicalName());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.gen;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Register {
|
||||
String id();
|
||||
String[] accept() default { };
|
||||
int priority() default Registry.Priority.MEDIUM;
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.gen;
|
||||
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class Registry<K, C, A> {
|
||||
|
||||
Set<Entry> entries = new TreeSet<>((o1, o2) -> {
|
||||
int cmp = o1.priority - o2.priority;
|
||||
if (cmp == 0)
|
||||
cmp = o1.id.compareToIgnoreCase(o2.id); // FIXME or not ... is id a good unique stuff
|
||||
return cmp;
|
||||
});
|
||||
|
||||
public class Priority {
|
||||
public static final int MAXIMUM = 0;
|
||||
public static final int HIGH = 25;
|
||||
public static final int MEDIUM = 50;
|
||||
public static final int LOW = 75;
|
||||
public static final int MINIMUM = 100;
|
||||
}
|
||||
|
||||
public C get(K key, Object... args) {
|
||||
Factory<? extends C> factory = getFactory(key);
|
||||
if (factory != null)
|
||||
return factory.instantiate(args);
|
||||
return null;
|
||||
}
|
||||
|
||||
public Factory<? extends C> getFactory(K key) {
|
||||
Entry entry = find(key);
|
||||
if (entry != null)
|
||||
return entry.factory;
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Entry find(K key) {
|
||||
Entry entry = findEntry(key);
|
||||
if (entry == null)
|
||||
return null;
|
||||
return entry;
|
||||
}
|
||||
|
||||
protected Entry findById(String id) {
|
||||
for (Entry e: entries)
|
||||
if (e.id.equals(id))
|
||||
return e;
|
||||
return null;
|
||||
}
|
||||
|
||||
public void install(Class<? extends C> clazz, A annotation) {
|
||||
Entry entry = newEntry(clazz, annotation);
|
||||
entries.add(entry);
|
||||
}
|
||||
|
||||
protected abstract Entry newEntry(Class<? extends C> clazz, A annotation);
|
||||
|
||||
protected Entry findEntry(K key) {
|
||||
for (Entry e: entries)
|
||||
if (e.handle(key))
|
||||
return e;
|
||||
return null;
|
||||
}
|
||||
|
||||
public Entry findByClass(Class<? extends C> aClass) {
|
||||
for (Entry e: entries)
|
||||
if (e.clazz.equals(aClass))
|
||||
return e;
|
||||
return null;
|
||||
}
|
||||
|
||||
public Set<Entry> getEntries() {
|
||||
return Collections.unmodifiableSet(entries);
|
||||
}
|
||||
|
||||
public abstract class Entry {
|
||||
public final String id;
|
||||
public final int priority;
|
||||
final Class<? extends C> clazz;
|
||||
final Factory<? extends C> factory;
|
||||
|
||||
protected Entry(String id, Class<? extends C> clazz, Factory<? extends C> factory, int priority) {
|
||||
this.id = id;
|
||||
this.clazz = clazz;
|
||||
this.factory = factory;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public C instantiate(Object[] args) {
|
||||
try {
|
||||
return factory.newInstance(args);
|
||||
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean handle(K key);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected Factory<? extends C> defaultFactory(Class<? extends C> clazz, Class... signature) {
|
||||
try {
|
||||
Constructor<? extends C> ctor = clazz.getConstructor(signature);
|
||||
return (args) -> ctor.newInstance(args);
|
||||
} catch (NoSuchMethodException e) {
|
||||
System.out.println(Arrays.toString(clazz.getConstructors()));
|
||||
throw new RuntimeException(String.format("This is a static bug. Constructor %s(%s) not found",
|
||||
clazz.getName(), Arrays.toString(signature)), e);
|
||||
}
|
||||
}
|
||||
|
||||
public interface Factory<C> {
|
||||
C newInstance(Object[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException;
|
||||
|
||||
default C instantiate(Object[] args) {
|
||||
try {
|
||||
return newInstance(args);
|
||||
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.gen;
|
||||
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public abstract class TreeGenerator {
|
||||
|
||||
protected abstract TreeContext generate(Reader r) throws IOException;
|
||||
|
||||
protected abstract TreeContext generate(Reader r, int astParserType) throws IOException;
|
||||
|
||||
public TreeContext generateFromReader(Reader r) throws IOException {
|
||||
TreeContext ctx = generate(r);
|
||||
ctx.validate();
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public TreeContext generateFromFile(String path) throws IOException {
|
||||
return generateFromReader(new FileReader(path));
|
||||
}
|
||||
|
||||
public TreeContext generateFromFile(File file) throws IOException {
|
||||
return generateFromReader(new FileReader(file));
|
||||
}
|
||||
|
||||
public TreeContext generateFromStream(InputStream stream) throws IOException {
|
||||
return generateFromReader(new InputStreamReader(stream));
|
||||
}
|
||||
|
||||
public TreeContext generateFromString(String content) throws IOException {
|
||||
return generateFromReader(new StringReader(content));
|
||||
}
|
||||
|
||||
public TreeContext generateFromReader(Reader r, int astParserType) throws IOException {
|
||||
TreeContext ctx = generate(r, astParserType);
|
||||
ctx.validate();
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public TreeContext generateFromFile(String path, int astParserType) throws IOException {
|
||||
return generateFromReader(new FileReader(path), astParserType);
|
||||
}
|
||||
|
||||
public TreeContext generateFromFile(File file, int astParserType) throws IOException {
|
||||
return generateFromReader(new FileReader(file), astParserType);
|
||||
}
|
||||
|
||||
public TreeContext generateFromStream(InputStream stream, int astParserType) throws IOException {
|
||||
return generateFromReader(new InputStreamReader(stream), astParserType);
|
||||
}
|
||||
|
||||
public TreeContext generateFromString(String content, int astParserType) throws IOException {
|
||||
return generateFromReader(new StringReader(content), astParserType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.io;
|
||||
|
||||
import com.github.gumtreediff.actions.model.*;
|
||||
import com.github.gumtreediff.io.TreeIoUtils.AbstractSerializer;
|
||||
import com.github.gumtreediff.matchers.Mapping;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.List;
|
||||
|
||||
public final class ActionsIoUtils {
|
||||
|
||||
private ActionsIoUtils() {
|
||||
}
|
||||
|
||||
public static ActionSerializer toText(TreeContext sctx, List<Action> actions,
|
||||
MappingStore mappings) throws IOException {
|
||||
return new ActionSerializer(sctx, mappings, actions) {
|
||||
|
||||
@Override
|
||||
protected ActionFormatter newFormatter(TreeContext ctx, Writer writer) throws Exception {
|
||||
return new TextFormatter(ctx, writer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static ActionSerializer toXml(TreeContext sctx, List<Action> actions,
|
||||
MappingStore mappings) throws IOException {
|
||||
return new ActionSerializer(sctx, mappings, actions) {
|
||||
|
||||
@Override
|
||||
protected ActionFormatter newFormatter(TreeContext ctx, Writer writer) throws Exception {
|
||||
return new XmlFormatter(ctx, writer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static ActionSerializer toJson(TreeContext sctx, List<Action> actions,
|
||||
MappingStore mappings) throws IOException {
|
||||
return new ActionSerializer(sctx, mappings, actions) {
|
||||
|
||||
@Override
|
||||
protected ActionFormatter newFormatter(TreeContext ctx, Writer writer) throws Exception {
|
||||
return new JsonFormatter(ctx, writer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public abstract static class ActionSerializer extends AbstractSerializer {
|
||||
final TreeContext context;
|
||||
final MappingStore mappings;
|
||||
final List<Action> actions;
|
||||
|
||||
ActionSerializer(TreeContext context, MappingStore mappings, List<Action> actions) {
|
||||
this.context = context;
|
||||
this.mappings = mappings;
|
||||
this.actions = actions;
|
||||
}
|
||||
|
||||
protected abstract ActionFormatter newFormatter(TreeContext ctx, Writer writer) throws Exception;
|
||||
|
||||
@Override
|
||||
public void writeTo(Writer writer) throws Exception {
|
||||
ActionFormatter fmt = newFormatter(context, writer);
|
||||
// Start the output
|
||||
fmt.startOutput();
|
||||
|
||||
// Write the matches
|
||||
fmt.startMatches();
|
||||
for (Mapping m: mappings) {
|
||||
fmt.match(m.getFirst(), m.getSecond());
|
||||
}
|
||||
fmt.endMatches();
|
||||
|
||||
// Write the actions
|
||||
fmt.startActions();
|
||||
for (Action a : actions) {
|
||||
ITree src = a.getNode();
|
||||
if (a instanceof Move) {
|
||||
ITree dst = mappings.getDst(src);
|
||||
fmt.moveAction(src, dst.getParent(), ((Move) a).getPosition());
|
||||
} else if (a instanceof Update) {
|
||||
ITree dst = mappings.getDst(src);
|
||||
fmt.updateAction(src, dst);
|
||||
} else if (a instanceof Insert) {
|
||||
ITree dst = a.getNode();
|
||||
if (dst.isRoot())
|
||||
fmt.insertRoot(src);
|
||||
else
|
||||
fmt.insertAction(src, dst.getParent(), dst.getParent().getChildPosition(dst));
|
||||
} else if (a instanceof Delete) {
|
||||
fmt.deleteAction(src);
|
||||
}
|
||||
}
|
||||
fmt.endActions();
|
||||
|
||||
// Finish up
|
||||
fmt.endOutput();
|
||||
}
|
||||
}
|
||||
|
||||
interface ActionFormatter {
|
||||
void startOutput() throws Exception;
|
||||
|
||||
void endOutput() throws Exception;
|
||||
|
||||
void startMatches() throws Exception;
|
||||
|
||||
void match(ITree srcNode, ITree destNode) throws Exception;
|
||||
|
||||
void endMatches() throws Exception;
|
||||
|
||||
void startActions() throws Exception;
|
||||
|
||||
void insertRoot(ITree node) throws Exception;
|
||||
|
||||
void insertAction(ITree node, ITree parent, int index) throws Exception;
|
||||
|
||||
void moveAction(ITree src, ITree dst, int index) throws Exception;
|
||||
|
||||
void updateAction(ITree src, ITree dst) throws Exception;
|
||||
|
||||
void deleteAction(ITree node) throws Exception;
|
||||
|
||||
void endActions() throws Exception;
|
||||
}
|
||||
|
||||
static class XmlFormatter implements ActionFormatter {
|
||||
final TreeContext context;
|
||||
final XMLStreamWriter writer;
|
||||
|
||||
XmlFormatter(TreeContext context, Writer w) throws XMLStreamException {
|
||||
XMLOutputFactory f = XMLOutputFactory.newInstance();
|
||||
writer = new IndentingXMLStreamWriter(f.createXMLStreamWriter(w));
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startOutput() throws XMLStreamException {
|
||||
writer.writeStartDocument();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endOutput() throws XMLStreamException {
|
||||
writer.writeEndDocument();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startMatches() throws XMLStreamException {
|
||||
writer.writeStartElement("matches");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void match(ITree srcNode, ITree destNode) throws XMLStreamException {
|
||||
writer.writeEmptyElement("match");
|
||||
writer.writeAttribute("src", Integer.toString(srcNode.getId()));
|
||||
writer.writeAttribute("dest", Integer.toString(destNode.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endMatches() throws XMLStreamException {
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActions() throws XMLStreamException {
|
||||
writer.writeStartElement("actions");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertRoot(ITree node) throws Exception {
|
||||
start(Insert.class, node);
|
||||
end(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertAction(ITree node, ITree parent, int index) throws Exception {
|
||||
start(Insert.class, node);
|
||||
writer.writeAttribute("parent", Integer.toString(parent.getId()));
|
||||
writer.writeAttribute("at", Integer.toString(index));
|
||||
end(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveAction(ITree src, ITree dst, int index) throws XMLStreamException {
|
||||
start(Move.class, src);
|
||||
writer.writeAttribute("parent", Integer.toString(dst.getId()));
|
||||
writer.writeAttribute("at", Integer.toString(index));
|
||||
end(src);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAction(ITree src, ITree dst) throws XMLStreamException {
|
||||
start(Update.class, src);
|
||||
writer.writeAttribute("label", dst.getLabel());
|
||||
end(src);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAction(ITree node) throws Exception {
|
||||
start(Delete.class, node);
|
||||
end(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endActions() throws XMLStreamException {
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
private void start(Class<? extends Action> name, ITree src) throws XMLStreamException {
|
||||
writer.writeEmptyElement(name.getSimpleName().toLowerCase());
|
||||
writer.writeAttribute("tree", Integer.toString(src.getId()));
|
||||
}
|
||||
|
||||
private void end(ITree node) throws XMLStreamException {
|
||||
// writer.writeEndElement();
|
||||
}
|
||||
}
|
||||
|
||||
static class TextFormatter implements ActionFormatter {
|
||||
final Writer writer;
|
||||
final TreeContext context;
|
||||
|
||||
public TextFormatter(TreeContext ctx, Writer writer) {
|
||||
this.context = ctx;
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startOutput() throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endOutput() throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startMatches() throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void match(ITree srcNode, ITree destNode) throws Exception {
|
||||
write("Match %s to %s", toS(srcNode), toS(destNode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endMatches() throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActions() throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertRoot(ITree node) throws Exception {
|
||||
write("Insert root %s", toS(node));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertAction(ITree node, ITree parent, int index) throws Exception {
|
||||
write("Insert %s into %s at %d", toS(node), toS(parent), index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveAction(ITree src, ITree dst, int position) throws Exception {
|
||||
write("Move %s into %s at %d", toS(src), toS(dst), position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAction(ITree src, ITree dst) throws Exception {
|
||||
write("Update %s to %s", toS(src), dst.getLabel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAction(ITree node) throws Exception {
|
||||
write("Delete %s", toS(node));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endActions() throws Exception {
|
||||
}
|
||||
|
||||
private void write(String fmt, Object... objs) throws IOException {
|
||||
writer.append(String.format(fmt, objs));
|
||||
writer.append("\n");
|
||||
}
|
||||
|
||||
private String toS(ITree node) {
|
||||
return String.format("%s(%d)", node.toPrettyString(context), node.getId());
|
||||
}
|
||||
}
|
||||
|
||||
static class JsonFormatter implements ActionFormatter {
|
||||
private final JsonWriter writer;
|
||||
|
||||
JsonFormatter(TreeContext ctx, Writer writer) {
|
||||
|
||||
this.writer = new JsonWriter(writer);
|
||||
this.writer.setIndent(" ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startOutput() throws IOException {
|
||||
writer.beginObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endOutput() throws IOException {
|
||||
writer.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startMatches() throws Exception {
|
||||
writer.name("matches").beginArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void match(ITree srcNode, ITree destNode) throws Exception {
|
||||
writer.beginObject();
|
||||
writer.name("src").value(srcNode.getId());
|
||||
writer.name("dest").value(destNode.getId());
|
||||
writer.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endMatches() throws Exception {
|
||||
writer.endArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActions() throws IOException {
|
||||
writer.name("actions").beginArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertRoot(ITree node) throws IOException {
|
||||
start(Insert.class, node);
|
||||
end(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertAction(ITree node, ITree parent, int index) throws IOException {
|
||||
start(Insert.class, node);
|
||||
writer.name("parent").value(parent.getId());
|
||||
writer.name("at").value(index);
|
||||
end(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveAction(ITree src, ITree dst, int index) throws IOException {
|
||||
start(Move.class, src);
|
||||
writer.name("parent").value(dst.getId());
|
||||
writer.name("at").value(index);
|
||||
end(src);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAction(ITree src, ITree dst) throws IOException {
|
||||
start(Update.class, src);
|
||||
writer.name("label").value(dst.getLabel());
|
||||
end(src);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAction(ITree node) throws IOException {
|
||||
start(Delete.class, node);
|
||||
end(node);
|
||||
}
|
||||
|
||||
private void start(Class<? extends Action> name, ITree src) throws IOException {
|
||||
writer.beginObject();
|
||||
writer.name("action").value(name.getSimpleName().toLowerCase());
|
||||
writer.name("tree").value(src.getId());
|
||||
}
|
||||
|
||||
private void end(ITree node) throws IOException {
|
||||
writer.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endActions() throws Exception {
|
||||
writer.endArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.github.gumtreediff.io;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class CNodeMap {
|
||||
|
||||
public static Map<Integer, String> map;
|
||||
|
||||
static {
|
||||
map = new HashMap<Integer, String>();
|
||||
map.put(20100,"Left");
|
||||
map.put(30100,"ActMisc");
|
||||
map.put(40000,"FullType");
|
||||
map.put(50000,"TypeQualifier");
|
||||
map.put(60100,"BaseType");
|
||||
map.put(60200,"Pointer");
|
||||
map.put(60800,"EnumName");
|
||||
map.put(60900,"StructUnionName");
|
||||
map.put(61000,"TypeName");
|
||||
map.put(70002,"SizeType");
|
||||
map.put(70100,"IntType");
|
||||
map.put(80001,"CChar");
|
||||
map.put(80100,"Si");
|
||||
map.put(90002,"UnSigned");
|
||||
map.put(100003,"CInt");
|
||||
map.put(200000,"ParamList");
|
||||
map.put(210000,"DotsParameter");
|
||||
map.put(220100,"ParameterType");
|
||||
map.put(240100,"Ident");
|
||||
map.put(240200,"Constant");
|
||||
map.put(240400,"FunCall");
|
||||
map.put(240500,"CondExpr");
|
||||
map.put(240600,"Sequence");
|
||||
map.put(240700,"Assignment");
|
||||
map.put(240800,"Postfix");
|
||||
map.put(240900,"Infix");
|
||||
map.put(241000,"Unary");
|
||||
map.put(241100,"Binary");
|
||||
map.put(241200,"ArrayAccess");
|
||||
map.put(241300,"RecordAccess");
|
||||
map.put(241400,"RecordPtAccess");
|
||||
map.put(241500,"SizeOfExpr");
|
||||
map.put(241600,"SizeOfType");
|
||||
map.put(241700,"Cast");
|
||||
map.put(241900,"Constructor");
|
||||
map.put(242000,"ParenExpr");
|
||||
map.put(260300,"ExprStatement");
|
||||
map.put(260800,"Asm");
|
||||
map.put(270100,"Label");
|
||||
map.put(270200,"Case");
|
||||
map.put(270300,"CaseRange");
|
||||
map.put(270400,"Default");
|
||||
map.put(280001,"Continue");
|
||||
map.put(280002,"Break");
|
||||
map.put(280003,"Return");
|
||||
map.put(280100,"Goto");
|
||||
map.put(280200,"ReturnExpr");
|
||||
map.put(290001,"None");
|
||||
map.put(290100,"Some");
|
||||
map.put(300100,"If");
|
||||
map.put(300200,"Switch");
|
||||
map.put(310100,"While");
|
||||
map.put(310200,"DoWhile");
|
||||
map.put(310300,"For");
|
||||
map.put(310400,"MacroIteration");
|
||||
map.put(330000,"Compound");
|
||||
map.put(340000,"Storage");
|
||||
map.put(350100,"DeclList");
|
||||
map.put(350200,"MacroDecl");
|
||||
map.put(360100,"InitExpr");
|
||||
map.put(360200,"InitList");
|
||||
map.put(360300,"InitDesignators");
|
||||
map.put(370100,"DesignatorField");
|
||||
map.put(370200,"DesignatorIndex");
|
||||
map.put(370300,"DesignatorRange");
|
||||
map.put(380000,"Definition");
|
||||
map.put(400100,"Define");
|
||||
map.put(400200,"Include");
|
||||
map.put(400400,"OtherDirective");
|
||||
map.put(410001,"DefineVar");
|
||||
map.put(410002,"Undef");
|
||||
map.put(410100,"DefineFunc");
|
||||
map.put(420001,"DefineEmpty");
|
||||
map.put(420100,"DefineExpr");
|
||||
map.put(420200,"DefineStmt");
|
||||
map.put(420400,"DefineDoWhileZero");
|
||||
map.put(420600,"DefineInit");
|
||||
map.put(440100,"IfdefDirective");
|
||||
map.put(450100,"Declaration");
|
||||
map.put(450200,"Definition");
|
||||
map.put(450300,"CppTop");
|
||||
map.put(450400,"IfdefTop");
|
||||
map.put(450700,"NotParsedCorrectly");
|
||||
map.put(450800,"FinalDef");
|
||||
map.put(460000,"Program");
|
||||
map.put(470000,"GenericList");
|
||||
map.put(480000,"GenericString");
|
||||
map.put(490100,"IfToken");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.io;
|
||||
|
||||
import com.github.gumtreediff.utils.Pair;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class DirectoryComparator {
|
||||
|
||||
private Path src;
|
||||
|
||||
public Path getSrc() {
|
||||
return src;
|
||||
}
|
||||
|
||||
public Path getDst() {
|
||||
return dst;
|
||||
}
|
||||
|
||||
private Path dst;
|
||||
|
||||
private List<Pair<File, File>> modifiedFiles;
|
||||
|
||||
private Set<File> deletedFiles;
|
||||
|
||||
private Set<File> addedFiles;
|
||||
|
||||
private boolean dirMode = true;
|
||||
|
||||
public DirectoryComparator(String src, String dst) {
|
||||
modifiedFiles = new ArrayList<>();
|
||||
addedFiles = new HashSet<>();
|
||||
deletedFiles = new HashSet<>();
|
||||
this.src = Paths.get(src);
|
||||
this.dst = Paths.get(dst);
|
||||
if (!Files.exists(this.src) || !Files.exists(this.dst))
|
||||
throw new RuntimeException();
|
||||
else {
|
||||
if (!Files.isDirectory(this.src) && !Files.isDirectory(this.dst)) {
|
||||
this.modifiedFiles.add(new Pair<>(this.src.toFile(), this.dst.toFile()));
|
||||
this.src = this.src.getParent();
|
||||
this.dst = this.dst.getParent();
|
||||
this.dirMode = false;
|
||||
} else if (!(Files.isDirectory(this.src) && Files.isDirectory(this.dst))) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void compare() {
|
||||
if (!dirMode) return;
|
||||
AllFilesVisitor vSrc = new AllFilesVisitor(src);
|
||||
AllFilesVisitor vDst = new AllFilesVisitor(dst);
|
||||
try {
|
||||
Files.walkFileTree(src, vSrc);
|
||||
Files.walkFileTree(dst, vDst);
|
||||
|
||||
Set<String> addedFiles = new HashSet<>();
|
||||
addedFiles.addAll(vDst.files);
|
||||
addedFiles.removeAll(vSrc.files);
|
||||
for (String file : addedFiles) this.addedFiles.add(toDstFile(file));
|
||||
|
||||
Set<String> deletedFiles = new HashSet<>();
|
||||
deletedFiles.addAll(vSrc.files);
|
||||
deletedFiles.removeAll(vDst.files);
|
||||
for (String file : deletedFiles) this.deletedFiles.add(toSrcFile(file));
|
||||
|
||||
Set<String> commonFiles = new HashSet<>();
|
||||
commonFiles.addAll(vSrc.files);
|
||||
commonFiles.retainAll(vDst.files);
|
||||
|
||||
for (String file : commonFiles)
|
||||
if (hasChanged(file, file))
|
||||
modifiedFiles.add(new Pair<File, File>(toSrcFile(file), toDstFile(file)));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDirMode() {
|
||||
return dirMode;
|
||||
}
|
||||
|
||||
public List<Pair<File, File>> getModifiedFiles() {
|
||||
return modifiedFiles;
|
||||
}
|
||||
|
||||
public Set<File> getDeletedFiles() {
|
||||
return deletedFiles;
|
||||
}
|
||||
|
||||
public Set<File> getAddedFiles() {
|
||||
return addedFiles;
|
||||
}
|
||||
|
||||
private File toSrcFile(String s) {
|
||||
return new File(src.toFile(), s);
|
||||
}
|
||||
|
||||
private File toDstFile(String s) {
|
||||
return new File(dst.toFile(), s);
|
||||
}
|
||||
|
||||
public boolean hasChanged(String s1, String s2) throws IOException {
|
||||
File f1 = toSrcFile(s1);
|
||||
File f2 = toDstFile(s2);
|
||||
long l1 = Files.size(f1.toPath());
|
||||
long l2 = Files.size(f2.toPath());
|
||||
if (l1 != l2) return true;
|
||||
else {
|
||||
try (DataInputStream dis1 = new DataInputStream(new FileInputStream(f1));
|
||||
DataInputStream dis2 = new DataInputStream(new FileInputStream(f2))) {
|
||||
int c1, c2;
|
||||
while ((c1 = dis1.read()) != -1) {
|
||||
c2 = dis2.read();
|
||||
if (c1 != c2)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class AllFilesVisitor extends SimpleFileVisitor<Path> {
|
||||
|
||||
private Set<String> files = new HashSet<>();
|
||||
|
||||
private Path root;
|
||||
|
||||
public AllFilesVisitor(Path root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (!file.getFileName().startsWith("."))
|
||||
files.add(root.relativize(file).toString());
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
|
||||
return (dir.getFileName().toString().startsWith("."))
|
||||
? FileVisitResult.SKIP_SUBTREE : FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2006, John Kristian
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of StAX-Utils nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
package com.github.gumtreediff.io;
|
||||
|
||||
/**
|
||||
* Characters that represent line breaks and indentation. These are represented
|
||||
* as String-valued JavaBean properties.
|
||||
*/
|
||||
public interface Indentation {
|
||||
|
||||
/** Two spaces; the default indentation. */
|
||||
public static final String DEFAULT_INDENT = " ";
|
||||
|
||||
/**
|
||||
* Set the characters used for one level of indentation. The default is
|
||||
* {@link #DEFAULT_INDENT}. "\t" is a popular alternative.
|
||||
*/
|
||||
void setIndent(String indent);
|
||||
|
||||
/** The characters used for one level of indentation. */
|
||||
String getIndent();
|
||||
|
||||
/**
|
||||
* "\n"; the normalized representation of end-of-line in <a
|
||||
* href="http://www.w3.org/TR/xml11/#sec-line-ends">XML</a>.
|
||||
*/
|
||||
public static final String NORMAL_END_OF_LINE = "\n";
|
||||
|
||||
/**
|
||||
* Set the characters that introduce a new line. The default is
|
||||
* {@link #NORMAL_END_OF_LINE}.
|
||||
* {@link IndentingXMLStreamWriter#getLineSeparator}() is a popular
|
||||
* alternative.
|
||||
*/
|
||||
public void setNewLine(String newLine);
|
||||
|
||||
/** The characters that introduce a new line. */
|
||||
String getNewLine();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* Copyright (c) 2006, John Kristian
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of StAX-Utils nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
package com.github.gumtreediff.io;
|
||||
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
|
||||
/**
|
||||
* A filter that indents an XML stream. To apply it, construct a filter that
|
||||
* contains another {@link XMLStreamWriter}, which you pass to the constructor.
|
||||
* Then call methods of the filter instead of the contained stream. For example:
|
||||
*
|
||||
* <pre>
|
||||
* {@link XMLStreamWriter} stream = ...
|
||||
* stream = new {@link IndentingXMLStreamWriter}(stream);
|
||||
* stream.writeStartDocument();
|
||||
* ...
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* The filter inserts characters to format the document as an outline, with
|
||||
* nested elements indented. Basically, it inserts a line break and whitespace
|
||||
* before:
|
||||
* <ul>
|
||||
* <li>each DTD, processing instruction or comment that's not preceded by data</li>
|
||||
* <li>each starting tag that's not preceded by data</li>
|
||||
* <li>each ending tag that's preceded by nested elements but not data</li>
|
||||
* </ul>
|
||||
* This works well with 'data-oriented' XML, wherein each element contains
|
||||
* either data or nested elements but not both. It can work badly with other
|
||||
* styles of XML. For example, the data in a 'mixed content' document are apt to
|
||||
* be polluted with indentation characters.
|
||||
*
|
||||
* <p>
|
||||
* Indentation can be adjusted by setting the newLine and indent properties. But
|
||||
* set them to whitespace only, for best results. Non-whitespace is apt to cause
|
||||
* problems, for example when this class attempts to insert newLine before the
|
||||
* root element.
|
||||
*
|
||||
* @author <a href="mailto:jk2006@engineer.com">John Kristian</a>
|
||||
*/
|
||||
public class IndentingXMLStreamWriter extends StreamWriterDelegate implements Indentation {
|
||||
|
||||
public IndentingXMLStreamWriter(XMLStreamWriter out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
/** How deeply nested the current scope is. The root element is depth 1. */
|
||||
private int depth = 0; // document scope
|
||||
|
||||
/** stack[depth] indicates what's been written into the current scope. */
|
||||
private int[] stack = new int[] { 0, 0, 0, 0 }; // nothing written yet
|
||||
|
||||
private static final int WROTE_MARKUP = 1;
|
||||
|
||||
private static final int WROTE_DATA = 2;
|
||||
|
||||
private String indent = DEFAULT_INDENT;
|
||||
|
||||
private String newLine = NORMAL_END_OF_LINE;
|
||||
|
||||
/** newLine followed by copies of indent. */
|
||||
private char[] linePrefix = null;
|
||||
|
||||
public void setIndent(String indent) {
|
||||
if (!indent.equals(this.indent)) {
|
||||
this.indent = indent;
|
||||
linePrefix = null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getIndent() {
|
||||
return indent;
|
||||
}
|
||||
|
||||
public void setNewLine(String newLine) {
|
||||
if (!newLine.equals(this.newLine)) {
|
||||
this.newLine = newLine;
|
||||
linePrefix = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return System.getProperty("line.separator"); or
|
||||
* {@link #NORMAL_END_OF_LINE} if that fails.
|
||||
*/
|
||||
public static String getLineSeparator() {
|
||||
try {
|
||||
return System.getProperty("line.separator");
|
||||
} catch (SecurityException ignored) {
|
||||
ignored.printStackTrace();
|
||||
}
|
||||
return NORMAL_END_OF_LINE;
|
||||
}
|
||||
|
||||
public String getNewLine() {
|
||||
return newLine;
|
||||
}
|
||||
|
||||
public void writeStartDocument() throws XMLStreamException {
|
||||
beforeMarkup();
|
||||
out.writeStartDocument();
|
||||
afterMarkup();
|
||||
}
|
||||
|
||||
public void writeStartDocument(String version) throws XMLStreamException {
|
||||
beforeMarkup();
|
||||
out.writeStartDocument(version);
|
||||
afterMarkup();
|
||||
}
|
||||
|
||||
public void writeStartDocument(String encoding, String version) throws XMLStreamException {
|
||||
beforeMarkup();
|
||||
out.writeStartDocument(encoding, version);
|
||||
afterMarkup();
|
||||
}
|
||||
|
||||
public void writeDTD(String dtd) throws XMLStreamException {
|
||||
beforeMarkup();
|
||||
out.writeDTD(dtd);
|
||||
afterMarkup();
|
||||
}
|
||||
|
||||
public void writeProcessingInstruction(String target) throws XMLStreamException {
|
||||
beforeMarkup();
|
||||
out.writeProcessingInstruction(target);
|
||||
afterMarkup();
|
||||
}
|
||||
|
||||
public void writeProcessingInstruction(String target, String data) throws XMLStreamException {
|
||||
beforeMarkup();
|
||||
out.writeProcessingInstruction(target, data);
|
||||
afterMarkup();
|
||||
}
|
||||
|
||||
public void writeComment(String data) throws XMLStreamException {
|
||||
beforeMarkup();
|
||||
out.writeComment(data);
|
||||
afterMarkup();
|
||||
}
|
||||
|
||||
public void writeEmptyElement(String localName) throws XMLStreamException {
|
||||
beforeMarkup();
|
||||
out.writeEmptyElement(localName);
|
||||
afterMarkup();
|
||||
}
|
||||
|
||||
public void writeEmptyElement(String namespaceURI, String localName) throws XMLStreamException {
|
||||
beforeMarkup();
|
||||
out.writeEmptyElement(namespaceURI, localName);
|
||||
afterMarkup();
|
||||
}
|
||||
|
||||
public void writeEmptyElement(String prefix, String localName, String namespaceURI)
|
||||
throws XMLStreamException {
|
||||
beforeMarkup();
|
||||
out.writeEmptyElement(prefix, localName, namespaceURI);
|
||||
afterMarkup();
|
||||
}
|
||||
|
||||
public void writeStartElement(String localName) throws XMLStreamException {
|
||||
beforeStartElement();
|
||||
out.writeStartElement(localName);
|
||||
afterStartElement();
|
||||
}
|
||||
|
||||
public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
|
||||
beforeStartElement();
|
||||
out.writeStartElement(namespaceURI, localName);
|
||||
afterStartElement();
|
||||
}
|
||||
|
||||
public void writeStartElement(String prefix, String localName, String namespaceURI)
|
||||
throws XMLStreamException {
|
||||
beforeStartElement();
|
||||
out.writeStartElement(prefix, localName, namespaceURI);
|
||||
afterStartElement();
|
||||
}
|
||||
|
||||
public void writeCharacters(String text) throws XMLStreamException {
|
||||
out.writeCharacters(text);
|
||||
afterData();
|
||||
}
|
||||
|
||||
public void writeCharacters(char[] text, int start, int len) throws XMLStreamException {
|
||||
out.writeCharacters(text, start, len);
|
||||
afterData();
|
||||
}
|
||||
|
||||
public void writeCData(String data) throws XMLStreamException {
|
||||
out.writeCData(data);
|
||||
afterData();
|
||||
}
|
||||
|
||||
public void writeEntityRef(String name) throws XMLStreamException {
|
||||
out.writeEntityRef(name);
|
||||
afterData();
|
||||
}
|
||||
|
||||
public void writeEndElement() throws XMLStreamException {
|
||||
beforeEndElement();
|
||||
out.writeEndElement();
|
||||
afterEndElement();
|
||||
}
|
||||
|
||||
public void writeEndDocument() throws XMLStreamException {
|
||||
try {
|
||||
while (depth > 0) {
|
||||
writeEndElement(); // indented
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
ignored.printStackTrace();
|
||||
}
|
||||
out.writeEndDocument();
|
||||
afterEndDocument();
|
||||
}
|
||||
|
||||
/** Prepare to write markup, by writing a new line and indentation. */
|
||||
protected void beforeMarkup() {
|
||||
int soFar = stack[depth];
|
||||
if ((soFar & WROTE_DATA) == 0 // no data in this scope
|
||||
&& (depth > 0 || soFar != 0)) {
|
||||
try {
|
||||
writeNewLine(depth);
|
||||
if (depth > 0 && getIndent().length() > 0) {
|
||||
afterMarkup(); // indentation was written
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Note that markup or indentation was written. */
|
||||
protected void afterMarkup() {
|
||||
stack[depth] |= WROTE_MARKUP;
|
||||
}
|
||||
|
||||
/** Note that data were written. */
|
||||
protected void afterData() {
|
||||
stack[depth] |= WROTE_DATA;
|
||||
}
|
||||
|
||||
/** Prepare to start an element, by allocating stack space. */
|
||||
protected void beforeStartElement() {
|
||||
beforeMarkup();
|
||||
if (stack.length <= depth + 1) {
|
||||
// Allocate more space for the stack:
|
||||
int[] newStack = new int[stack.length * 2];
|
||||
System.arraycopy(stack, 0, newStack, 0, stack.length);
|
||||
stack = newStack;
|
||||
}
|
||||
stack[depth + 1] = 0; // nothing written yet
|
||||
}
|
||||
|
||||
/** Note that an element was started. */
|
||||
protected void afterStartElement() {
|
||||
afterMarkup();
|
||||
++depth;
|
||||
}
|
||||
|
||||
/** Prepare to end an element, by writing a new line and indentation. */
|
||||
protected void beforeEndElement() {
|
||||
if (depth > 0 && stack[depth] == WROTE_MARKUP) { // but not data
|
||||
try {
|
||||
writeNewLine(depth - 1);
|
||||
} catch (Exception ignored) {
|
||||
ignored.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Note that an element was ended. */
|
||||
protected void afterEndElement() {
|
||||
if (depth > 0) {
|
||||
--depth;
|
||||
}
|
||||
}
|
||||
|
||||
/** Note that a document was ended. */
|
||||
protected void afterEndDocument() {
|
||||
if (stack[depth = 0] == WROTE_MARKUP) { // but not data
|
||||
try {
|
||||
writeNewLine(0);
|
||||
} catch (Exception ignored) {
|
||||
ignored.printStackTrace();
|
||||
}
|
||||
}
|
||||
stack[depth] = 0; // start fresh
|
||||
}
|
||||
|
||||
/** Write a line separator followed by indentation. */
|
||||
protected void writeNewLine(int indentation) throws XMLStreamException {
|
||||
final int newLineLength = getNewLine().length();
|
||||
final int prefixLength = newLineLength + (getIndent().length() * indentation);
|
||||
if (prefixLength > 0) {
|
||||
if (linePrefix == null) {
|
||||
linePrefix = (getNewLine() + getIndent()).toCharArray();
|
||||
}
|
||||
while (prefixLength > linePrefix.length) {
|
||||
// make linePrefix longer:
|
||||
char[] newPrefix = new char[newLineLength
|
||||
+ ((linePrefix.length - newLineLength) * 2)];
|
||||
System.arraycopy(linePrefix, 0, newPrefix, 0, linePrefix.length);
|
||||
System.arraycopy(linePrefix, newLineLength, newPrefix, linePrefix.length,
|
||||
linePrefix.length - newLineLength);
|
||||
linePrefix = newPrefix;
|
||||
}
|
||||
out.writeCharacters(linePrefix, 0, prefixLength);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class LineReader extends Reader {
|
||||
private Reader reader;
|
||||
|
||||
private int currentPos = 0;
|
||||
|
||||
private ArrayList<Integer> lines = new ArrayList<>(Arrays.asList(0));
|
||||
|
||||
public LineReader(Reader parent) {
|
||||
reader = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(char[] cbuf, int off, int len) throws IOException {
|
||||
int r = reader.read(cbuf, off, len);
|
||||
for (int i = 0; i < len; i ++)
|
||||
if (cbuf[off + i] == '\n')
|
||||
lines.add(currentPos + i);
|
||||
currentPos += len;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Line and column starts at 1
|
||||
public int positionFor(int line, int column) {
|
||||
return lines.get(line - 1) + column - 1;
|
||||
}
|
||||
|
||||
// public int[] positionFor(int offset) { // TODO write this method
|
||||
// Arrays.binarySearch(lines., null, null)
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.io;
|
||||
|
||||
public final class MatrixDebugger {
|
||||
|
||||
private MatrixDebugger() {
|
||||
}
|
||||
|
||||
public static void dump(Object[][] mat) {
|
||||
for (Object[] r : mat) {
|
||||
for (Object l: r) System.out.print(l + "\t");
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
public static void dump(boolean[][] mat) {
|
||||
for (boolean[] r : mat) {
|
||||
for (boolean l: r) System.out.print(l + "\t");
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
public static void dump(double[][] mat) {
|
||||
System.out.println("---");
|
||||
for (double[] r : mat) {
|
||||
for (double l: r) System.out.print(l + "\t");
|
||||
System.out.println();
|
||||
}
|
||||
System.out.println("---");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (c) 2006, John Kristian
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of StAX-Utils nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
package com.github.gumtreediff.io;
|
||||
|
||||
import javax.xml.namespace.NamespaceContext;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
|
||||
/**
|
||||
* Abstract class for writing filtered XML streams. This class provides methods
|
||||
* that merely delegate to the contained stream. Subclasses should override some
|
||||
* of these methods, and may also provide additional methods and fields.
|
||||
*
|
||||
* @author <a href="mailto:jk2006@engineer.com">John Kristian</a>
|
||||
*/
|
||||
public abstract class StreamWriterDelegate implements XMLStreamWriter {
|
||||
|
||||
protected StreamWriterDelegate(XMLStreamWriter out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
protected XMLStreamWriter out;
|
||||
|
||||
public Object getProperty(String name) throws IllegalArgumentException {
|
||||
return out.getProperty(name);
|
||||
}
|
||||
|
||||
public NamespaceContext getNamespaceContext() {
|
||||
return out.getNamespaceContext();
|
||||
}
|
||||
|
||||
public void setNamespaceContext(NamespaceContext context) throws XMLStreamException {
|
||||
out.setNamespaceContext(context);
|
||||
}
|
||||
|
||||
public void setDefaultNamespace(String uri) throws XMLStreamException {
|
||||
out.setDefaultNamespace(uri);
|
||||
}
|
||||
|
||||
public void writeStartDocument() throws XMLStreamException {
|
||||
out.writeStartDocument();
|
||||
}
|
||||
|
||||
public void writeStartDocument(String version) throws XMLStreamException {
|
||||
out.writeStartDocument(version);
|
||||
}
|
||||
|
||||
public void writeStartDocument(String encoding, String version) throws XMLStreamException {
|
||||
out.writeStartDocument(encoding, version);
|
||||
}
|
||||
|
||||
public void writeDTD(String dtd) throws XMLStreamException {
|
||||
out.writeDTD(dtd);
|
||||
}
|
||||
|
||||
public void writeProcessingInstruction(String target) throws XMLStreamException {
|
||||
out.writeProcessingInstruction(target);
|
||||
}
|
||||
|
||||
public void writeProcessingInstruction(String target, String data) throws XMLStreamException {
|
||||
out.writeProcessingInstruction(target, data);
|
||||
}
|
||||
|
||||
public void writeComment(String data) throws XMLStreamException {
|
||||
out.writeComment(data);
|
||||
}
|
||||
|
||||
public void writeEmptyElement(String localName) throws XMLStreamException {
|
||||
out.writeEmptyElement(localName);
|
||||
}
|
||||
|
||||
public void writeEmptyElement(String namespaceURI, String localName) throws XMLStreamException {
|
||||
out.writeEmptyElement(namespaceURI, localName);
|
||||
}
|
||||
|
||||
public void writeEmptyElement(String prefix, String localName, String namespaceURI)
|
||||
throws XMLStreamException {
|
||||
out.writeEmptyElement(prefix, localName, namespaceURI);
|
||||
}
|
||||
|
||||
public void writeStartElement(String localName) throws XMLStreamException {
|
||||
out.writeStartElement(localName);
|
||||
}
|
||||
|
||||
public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
|
||||
out.writeStartElement(namespaceURI, localName);
|
||||
}
|
||||
|
||||
public void writeStartElement(String prefix, String localName, String namespaceURI)
|
||||
throws XMLStreamException {
|
||||
out.writeStartElement(prefix, localName, namespaceURI);
|
||||
}
|
||||
|
||||
public void writeDefaultNamespace(String namespaceURI) throws XMLStreamException {
|
||||
out.writeDefaultNamespace(namespaceURI);
|
||||
}
|
||||
|
||||
public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException {
|
||||
out.writeNamespace(prefix, namespaceURI);
|
||||
}
|
||||
|
||||
public String getPrefix(String uri) throws XMLStreamException {
|
||||
return out.getPrefix(uri);
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix, String uri) throws XMLStreamException {
|
||||
out.setPrefix(prefix, uri);
|
||||
}
|
||||
|
||||
public void writeAttribute(String localName, String value) throws XMLStreamException {
|
||||
out.writeAttribute(localName, value);
|
||||
}
|
||||
|
||||
public void writeAttribute(String namespaceURI, String localName, String value)
|
||||
throws XMLStreamException {
|
||||
out.writeAttribute(namespaceURI, localName, value);
|
||||
}
|
||||
|
||||
public void writeAttribute(String prefix, String namespaceURI, String localName, String value)
|
||||
throws XMLStreamException {
|
||||
out.writeAttribute(prefix, namespaceURI, localName, value);
|
||||
}
|
||||
|
||||
public void writeCharacters(String text) throws XMLStreamException {
|
||||
out.writeCharacters(text);
|
||||
}
|
||||
|
||||
public void writeCharacters(char[] text, int start, int len) throws XMLStreamException {
|
||||
out.writeCharacters(text, start, len);
|
||||
}
|
||||
|
||||
public void writeCData(String data) throws XMLStreamException {
|
||||
out.writeCData(data);
|
||||
}
|
||||
|
||||
public void writeEntityRef(String name) throws XMLStreamException {
|
||||
out.writeEntityRef(name);
|
||||
}
|
||||
|
||||
public void writeEndElement() throws XMLStreamException {
|
||||
out.writeEndElement();
|
||||
}
|
||||
|
||||
public void writeEndDocument() throws XMLStreamException {
|
||||
out.writeEndDocument();
|
||||
}
|
||||
|
||||
public void flush() throws XMLStreamException {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public void close() throws XMLStreamException {
|
||||
out.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,699 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.io;
|
||||
|
||||
import com.github.gumtreediff.gen.Register;
|
||||
import com.github.gumtreediff.gen.TreeGenerator;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
import com.github.gumtreediff.tree.TreeContext.MetadataSerializers;
|
||||
import com.github.gumtreediff.tree.TreeContext.MetadataUnserializers;
|
||||
import com.github.gumtreediff.tree.TreeUtils;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.stream.*;
|
||||
import javax.xml.stream.events.Attribute;
|
||||
import javax.xml.stream.events.EndElement;
|
||||
import javax.xml.stream.events.StartElement;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class TreeIoUtils {
|
||||
|
||||
private TreeIoUtils() {} // Forbids instantiation of TreeIOUtils
|
||||
|
||||
public static TreeGenerator fromXml() {
|
||||
return new XmlInternalGenerator();
|
||||
}
|
||||
|
||||
public static TreeGenerator fromXml(MetadataUnserializers unserializers) {
|
||||
XmlInternalGenerator generator = new XmlInternalGenerator();
|
||||
generator.getUnserializers().addAll(unserializers);
|
||||
return generator;
|
||||
}
|
||||
|
||||
public static TreeSerializer toXml(TreeContext ctx) {
|
||||
return new TreeSerializer(ctx) {
|
||||
@Override
|
||||
protected TreeFormatter newFormatter(TreeContext ctx, MetadataSerializers serializers, Writer writer)
|
||||
throws XMLStreamException {
|
||||
return new XmlFormatter(writer, ctx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static TreeSerializer toAnnotatedXml(TreeContext ctx, boolean isSrc, MappingStore m) {
|
||||
return new TreeSerializer(ctx) {
|
||||
@Override
|
||||
protected TreeFormatter newFormatter(TreeContext ctx, MetadataSerializers serializers, Writer writer)
|
||||
throws XMLStreamException {
|
||||
return new XmlAnnotatedFormatter(writer, ctx, isSrc, m);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static TreeSerializer toCompactXml(TreeContext ctx) {
|
||||
return new TreeSerializer(ctx) {
|
||||
@Override
|
||||
protected TreeFormatter newFormatter(TreeContext ctx, MetadataSerializers serializers, Writer writer)
|
||||
throws Exception {
|
||||
return new XmlCompactFormatter(writer, ctx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static TreeSerializer toJson(TreeContext ctx) {
|
||||
return new TreeSerializer(ctx) {
|
||||
@Override
|
||||
protected TreeFormatter newFormatter(TreeContext ctx, MetadataSerializers serializers, Writer writer)
|
||||
throws Exception {
|
||||
return new JsonFormatter(writer, ctx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static TreeSerializer toLisp(TreeContext ctx) {
|
||||
return new TreeSerializer(ctx) {
|
||||
@Override
|
||||
protected TreeFormatter newFormatter(TreeContext ctx, MetadataSerializers serializers, Writer writer)
|
||||
throws Exception {
|
||||
return new LispFormatter(writer, ctx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static TreeSerializer toDot(TreeContext ctx) {
|
||||
return new TreeSerializer(ctx) {
|
||||
@Override
|
||||
protected TreeFormatter newFormatter(TreeContext ctx, MetadataSerializers serializer, Writer writer)
|
||||
throws Exception {
|
||||
return new DotFormatter(writer, ctx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public abstract static class AbstractSerializer {
|
||||
|
||||
public abstract void writeTo(Writer writer) throws Exception;
|
||||
|
||||
public void writeTo(OutputStream writer) throws Exception {
|
||||
// FIXME Since the stream is already open, we should not close it, however due to semantic issue
|
||||
// it should stay like this
|
||||
try (OutputStreamWriter os = new OutputStreamWriter(writer)) {
|
||||
writeTo(os);
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
try (StringWriter s = new StringWriter()) {
|
||||
writeTo(s);
|
||||
return s.toString();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTo(String file) throws Exception {
|
||||
try (FileWriter w = new FileWriter(file)) {
|
||||
writeTo(w);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTo(File file) throws Exception {
|
||||
try (FileWriter w = new FileWriter(file)) {
|
||||
writeTo(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class TreeSerializer extends AbstractSerializer {
|
||||
final TreeContext context;
|
||||
final MetadataSerializers serializers = new MetadataSerializers();
|
||||
|
||||
public TreeSerializer(TreeContext ctx) {
|
||||
context = ctx;
|
||||
serializers.addAll(ctx.getSerializers());
|
||||
}
|
||||
|
||||
protected abstract TreeFormatter newFormatter(TreeContext ctx, MetadataSerializers serializers, Writer writer)
|
||||
throws Exception;
|
||||
|
||||
public void writeTo(Writer writer) throws Exception {
|
||||
TreeFormatter formatter = newFormatter(context, serializers, writer);
|
||||
try {
|
||||
writeTree(formatter, context.getRoot());
|
||||
} finally {
|
||||
formatter.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void forwardException(Exception e) {
|
||||
throw new FormatException(e);
|
||||
}
|
||||
|
||||
protected void writeTree(TreeFormatter formatter, ITree root) throws Exception {
|
||||
formatter.startSerialization();
|
||||
writeAttributes(formatter, context.getMetadata());
|
||||
formatter.endProlog();
|
||||
try {
|
||||
TreeUtils.visitTree(root, new TreeUtils.TreeVisitor() {
|
||||
|
||||
@Override
|
||||
public void startTree(ITree tree) {
|
||||
try {
|
||||
assert tree != null;
|
||||
formatter.startTree(tree);
|
||||
writeAttributes(formatter, tree.getMetadata());
|
||||
formatter.endTreeProlog(tree);
|
||||
} catch (Exception e) {
|
||||
forwardException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTree(ITree tree) {
|
||||
try {
|
||||
formatter.endTree(tree);
|
||||
} catch (Exception e) {
|
||||
forwardException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (FormatException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
formatter.stopSerialization();
|
||||
}
|
||||
|
||||
protected void writeAttributes(TreeFormatter formatter, Iterator<Entry<String, Object>> it) throws Exception {
|
||||
while (it.hasNext()) {
|
||||
Entry<String, Object> entry = it.next();
|
||||
serializers.serialize(formatter, entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public TreeSerializer export(String name, MetadataSerializer serializer) {
|
||||
serializers.add(name, serializer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TreeSerializer export(String... name) {
|
||||
for (String n: name)
|
||||
serializers.add(n, Object::toString);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public interface TreeFormatter {
|
||||
void startSerialization() throws Exception;
|
||||
|
||||
void endProlog() throws Exception;
|
||||
|
||||
void stopSerialization() throws Exception;
|
||||
|
||||
void startTree(ITree tree) throws Exception;
|
||||
|
||||
void endTreeProlog(ITree tree) throws Exception;
|
||||
|
||||
void endTree(ITree tree) throws Exception;
|
||||
|
||||
void close() throws Exception;
|
||||
|
||||
void serializeAttribute(String name, String value) throws Exception;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MetadataSerializer {
|
||||
String toString(Object object);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MetadataUnserializer {
|
||||
Object fromString(String value);
|
||||
}
|
||||
|
||||
static class FormatException extends RuntimeException {
|
||||
private static final long serialVersionUID = 593766540545763066L;
|
||||
Exception cause;
|
||||
|
||||
public FormatException(Exception cause) {
|
||||
super(cause);
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exception getCause() {
|
||||
return cause;
|
||||
}
|
||||
}
|
||||
|
||||
static class TreeFormatterAdapter implements TreeFormatter {
|
||||
protected final TreeContext context;
|
||||
|
||||
protected TreeFormatterAdapter(TreeContext ctx) {
|
||||
context = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSerialization() throws Exception { }
|
||||
|
||||
@Override
|
||||
public void endProlog() throws Exception { }
|
||||
|
||||
@Override
|
||||
public void startTree(ITree tree) throws Exception { }
|
||||
|
||||
@Override
|
||||
public void endTreeProlog(ITree tree) throws Exception { }
|
||||
|
||||
@Override
|
||||
public void endTree(ITree tree) throws Exception { }
|
||||
|
||||
@Override
|
||||
public void stopSerialization() throws Exception { }
|
||||
|
||||
@Override
|
||||
public void close() throws Exception { }
|
||||
|
||||
@Override
|
||||
public void serializeAttribute(String name, String value) throws Exception { }
|
||||
}
|
||||
|
||||
abstract static class AbsXmlFormatter extends TreeFormatterAdapter {
|
||||
protected final XMLStreamWriter writer;
|
||||
|
||||
protected AbsXmlFormatter(Writer w, TreeContext ctx) throws XMLStreamException {
|
||||
super(ctx);
|
||||
XMLOutputFactory f = XMLOutputFactory.newInstance();
|
||||
writer = new IndentingXMLStreamWriter(f.createXMLStreamWriter(w));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSerialization() throws XMLStreamException {
|
||||
writer.writeStartDocument();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopSerialization() throws XMLStreamException {
|
||||
writer.writeEndDocument();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws XMLStreamException {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
static class XmlFormatter extends AbsXmlFormatter {
|
||||
public XmlFormatter(Writer w, TreeContext ctx) throws XMLStreamException {
|
||||
super(w, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSerialization() throws XMLStreamException {
|
||||
super.startSerialization();
|
||||
writer.writeStartElement("root");
|
||||
writer.writeStartElement("context");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endProlog() throws XMLStreamException {
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopSerialization() throws XMLStreamException {
|
||||
writer.writeEndElement();
|
||||
super.stopSerialization();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeAttribute(String name, String value) throws XMLStreamException {
|
||||
writer.writeStartElement(name);
|
||||
writer.writeCharacters(value);
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTree(ITree tree) throws XMLStreamException {
|
||||
writer.writeStartElement("tree");
|
||||
writer.writeAttribute("type", Integer.toString(tree.getType()));
|
||||
if (tree.hasLabel()) writer.writeAttribute("label", tree.getLabel());
|
||||
if (context.hasLabelFor(tree.getType()))
|
||||
writer.writeAttribute("typeLabel", context.getTypeLabel(tree.getType()));
|
||||
if (ITree.NO_VALUE != tree.getPos()) {
|
||||
writer.writeAttribute("pos", Integer.toString(tree.getPos()));
|
||||
writer.writeAttribute("length", Integer.toString(tree.getLength()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTree(ITree tree) throws XMLStreamException {
|
||||
writer.writeEndElement();
|
||||
}
|
||||
}
|
||||
|
||||
static class XmlAnnotatedFormatter extends XmlFormatter {
|
||||
final SearchOther searchOther;
|
||||
|
||||
public XmlAnnotatedFormatter(Writer w, TreeContext ctx, boolean isSrc,
|
||||
MappingStore m) throws XMLStreamException {
|
||||
super(w, ctx);
|
||||
|
||||
if (isSrc)
|
||||
searchOther = (tree) -> m.hasSrc(tree) ? m.getDst(tree) : null;
|
||||
else
|
||||
searchOther = (tree) -> m.hasDst(tree) ? m.getSrc(tree) : null;
|
||||
}
|
||||
|
||||
interface SearchOther {
|
||||
ITree lookup(ITree tree);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTree(ITree tree) throws XMLStreamException {
|
||||
super.startTree(tree);
|
||||
ITree o = searchOther.lookup(tree);
|
||||
|
||||
if (o != null) {
|
||||
if (ITree.NO_VALUE != o.getPos()) {
|
||||
writer.writeAttribute("other_pos", Integer.toString(o.getPos()));
|
||||
writer.writeAttribute("other_length", Integer.toString(o.getLength()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class XmlCompactFormatter extends AbsXmlFormatter {
|
||||
public XmlCompactFormatter(Writer w, TreeContext ctx) throws XMLStreamException {
|
||||
super(w, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSerialization() throws XMLStreamException {
|
||||
super.startSerialization();
|
||||
writer.writeStartElement("root");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopSerialization() throws XMLStreamException {
|
||||
writer.writeEndElement();
|
||||
super.stopSerialization();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeAttribute(String name, String value) throws XMLStreamException {
|
||||
writer.writeAttribute(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTree(ITree tree) throws XMLStreamException {
|
||||
if (tree.getChildren().size() == 0)
|
||||
writer.writeEmptyElement(context.getTypeLabel(tree.getType()));
|
||||
else
|
||||
writer.writeStartElement(context.getTypeLabel(tree.getType()));
|
||||
if (tree.hasLabel())
|
||||
writer.writeAttribute("label", tree.getLabel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTree(ITree tree) throws XMLStreamException {
|
||||
if (tree.getChildren().size() > 0)
|
||||
writer.writeEndElement();
|
||||
}
|
||||
}
|
||||
|
||||
static class LispFormatter extends TreeFormatterAdapter {
|
||||
protected final Writer writer;
|
||||
protected final Pattern replacer = Pattern.compile("[\\\\\"]");
|
||||
int level = 0;
|
||||
|
||||
protected LispFormatter(Writer w, TreeContext ctx) {
|
||||
super(ctx);
|
||||
writer = w;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSerialization() throws IOException {
|
||||
writer.write("((");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTree(ITree tree) throws IOException {
|
||||
if (!tree.isRoot())
|
||||
writer.write("\n");
|
||||
for (int i = 0; i < level; i ++)
|
||||
writer.write(" ");
|
||||
level ++;
|
||||
|
||||
String pos = (ITree.NO_VALUE == tree.getPos() ? "" : String.format("(%d %d)",
|
||||
tree.getPos(), tree.getLength()));
|
||||
|
||||
writer.write(String.format("(%d %s %s (%s",
|
||||
tree.getType(), protect(context.getTypeLabel(tree)), protect(tree.getLabel()), pos));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endProlog() throws Exception {
|
||||
writer.append(") ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTreeProlog(ITree tree) throws Exception {
|
||||
writer.append(") (");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeAttribute(String name, String value) throws Exception {
|
||||
writer.append(String.format("(:%s %s) ", name, protect(value)));
|
||||
}
|
||||
|
||||
protected String protect(String val) {
|
||||
return String.format("\"%s\"", replacer.matcher(val).replaceAll("\\\\$0"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTree(ITree tree) throws IOException {
|
||||
writer.write(")");
|
||||
level --;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopSerialization() throws IOException {
|
||||
writer.write(")");
|
||||
}
|
||||
}
|
||||
|
||||
static class DotFormatter extends TreeFormatterAdapter {
|
||||
protected final Writer writer;
|
||||
|
||||
protected DotFormatter(Writer w, TreeContext ctx) {
|
||||
super(ctx);
|
||||
writer = w;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSerialization() throws Exception {
|
||||
writer.write("digraph G {\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTree(ITree tree) throws Exception {
|
||||
String label = tree.toPrettyString(context);
|
||||
if (label.contains("\"") || label.contains("\\s"))
|
||||
label = label.replaceAll("\"", "").replaceAll("\\s", "").replaceAll("\\\\", "");
|
||||
if (label.length() > 30)
|
||||
label = label.substring(0, 30);
|
||||
writer.write(tree.getId() + " [label=\"" + label + "\"];\n");
|
||||
|
||||
if (tree.getParent() != null)
|
||||
writer.write(tree.getParent().getId() + " -> " + tree.getId() + ";\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopSerialization() throws Exception {
|
||||
writer.write("}");
|
||||
}
|
||||
}
|
||||
|
||||
static class JsonFormatter extends TreeFormatterAdapter {
|
||||
private final JsonWriter writer;
|
||||
|
||||
public JsonFormatter(Writer w, TreeContext ctx) {
|
||||
super(ctx);
|
||||
writer = new JsonWriter(w);
|
||||
writer.setIndent(" ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTree(ITree t) throws IOException {
|
||||
writer.beginObject();
|
||||
writer.name("type").value(Integer.toString(t.getType()));
|
||||
if (t.hasLabel()) writer.name("label").value(t.getLabel());
|
||||
if (context.hasLabelFor(t.getType())) writer.name("typeLabel").value(context.getTypeLabel(t.getType()));
|
||||
if (ITree.NO_VALUE != t.getPos()) {
|
||||
writer.name("pos").value(Integer.toString(t.getPos()));
|
||||
writer.name("length").value(Integer.toString(t.getLength()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTreeProlog(ITree tree) throws IOException {
|
||||
writer.name("children");
|
||||
writer.beginArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTree(ITree tree) throws IOException {
|
||||
writer.endArray();
|
||||
writer.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSerialization() throws IOException {
|
||||
writer.beginObject();
|
||||
writer.setIndent("\t");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endProlog() throws IOException {
|
||||
writer.name("root");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeAttribute(String key, String value) throws IOException {
|
||||
writer.name(key).value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopSerialization() throws IOException {
|
||||
writer.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Register(id = "xml", accept = "\\.gxml$")
|
||||
// TODO Since it is not in the right package, I'm not even sure it is visible in the registry
|
||||
// TODO should we move this class elsewhere (another package)
|
||||
public static class XmlInternalGenerator extends TreeGenerator {
|
||||
|
||||
static MetadataUnserializers defaultUnserializers = new MetadataUnserializers();
|
||||
final MetadataUnserializers unserializers = new MetadataUnserializers(); // FIXME should it be pushed up or not?
|
||||
|
||||
private static final QName TYPE = new QName("type");
|
||||
|
||||
private static final QName LABEL = new QName("label");
|
||||
private static final QName TYPE_LABEL = new QName("typeLabel");
|
||||
private static final String POS = "pos";
|
||||
private static final String LENGTH = "length";
|
||||
private static final String LINE_BEFORE = "line_before";
|
||||
private static final String LINE_AFTER = "line_after";
|
||||
|
||||
static {
|
||||
// defaultUnserializers.add(POS, Integer::parseInt);
|
||||
// defaultUnserializers.add(LENGTH, Integer::parseInt);
|
||||
defaultUnserializers.add(LINE_BEFORE, Integer::parseInt);
|
||||
defaultUnserializers.add(LINE_AFTER, Integer::parseInt);
|
||||
}
|
||||
|
||||
public static <T, E> List<T> getKeysByValue(Map<T, E> map, E value) {
|
||||
return map.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> Objects.equals(entry.getValue(), value))
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public XmlInternalGenerator() {
|
||||
unserializers.addAll(defaultUnserializers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TreeContext generate(Reader source) throws IOException {
|
||||
XMLInputFactory fact = XMLInputFactory.newInstance();
|
||||
TreeContext context = new TreeContext();
|
||||
try {
|
||||
Stack<ITree> trees = new Stack<>();
|
||||
XMLEventReader r = fact.createXMLEventReader(source);
|
||||
while (r.hasNext()) {
|
||||
XMLEvent e = r.nextEvent();
|
||||
if (e instanceof StartElement) {
|
||||
StartElement s = (StartElement) e;
|
||||
if (!s.getName().getLocalPart().equals("tree")) // FIXME need to deal with options
|
||||
continue;
|
||||
// int type = Integer.parseInt(s.getAttributeByName(TYPE).getValue());
|
||||
List<Integer> keysByValue = getKeysByValue(CNodeMap.map, s.getAttributeByName(TYPE).getValue());
|
||||
int type = keysByValue.get(0);
|
||||
|
||||
|
||||
ITree t = context.createTree(type,
|
||||
labelForAttribute(s, LABEL), labelForAttribute(s, TYPE_LABEL));
|
||||
// FIXME this iterator has no type, due to the API. We have to cast it later
|
||||
Iterator<?> it = s.getAttributes();
|
||||
while (it.hasNext()) {
|
||||
Attribute a = (Attribute) it.next();
|
||||
unserializers.load(t, a.getName().getLocalPart(), a.getValue());
|
||||
}
|
||||
|
||||
if (trees.isEmpty())
|
||||
context.setRoot(t);
|
||||
else
|
||||
t.setParentAndUpdateChildren(trees.peek());
|
||||
trees.push(t);
|
||||
} else if (e instanceof EndElement) {
|
||||
if (!((EndElement)e).getName().getLocalPart().equals("tree")) // FIXME need to deal with options
|
||||
continue;
|
||||
trees.pop();
|
||||
}
|
||||
}
|
||||
context.validate();
|
||||
return context;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TreeContext generate(Reader source, int astParserType) throws IOException {
|
||||
return generate(source);
|
||||
}
|
||||
|
||||
private static String labelForAttribute(StartElement s, QName attrName) {
|
||||
Attribute attr = s.getAttributeByName(attrName);
|
||||
return attr == null ? ITree.NO_LABEL : attr.getValue();
|
||||
}
|
||||
|
||||
public MetadataUnserializers getUnserializers() {
|
||||
return unserializers;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
public class CompositeMatcher extends Matcher {
|
||||
|
||||
protected final Matcher[] matchers;
|
||||
|
||||
public CompositeMatcher(ITree src, ITree dst, MappingStore store, Matcher[] matchers) {
|
||||
super(src, dst, store);
|
||||
this.matchers = matchers;
|
||||
}
|
||||
|
||||
public void match() {
|
||||
for (Matcher matcher : matchers) {
|
||||
matcher.match();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers;
|
||||
|
||||
import com.github.gumtreediff.gen.Registry;
|
||||
import com.github.gumtreediff.matchers.heuristic.cd.ChangeDistillerBottomUpMatcher;
|
||||
import com.github.gumtreediff.matchers.heuristic.cd.ChangeDistillerLeavesMatcher;
|
||||
import com.github.gumtreediff.matchers.heuristic.gt.CompleteBottomUpMatcher;
|
||||
import com.github.gumtreediff.matchers.heuristic.XyBottomUpMatcher;
|
||||
import com.github.gumtreediff.matchers.heuristic.gt.CliqueSubtreeMatcher;
|
||||
import com.github.gumtreediff.matchers.heuristic.gt.GreedyBottomUpMatcher;
|
||||
import com.github.gumtreediff.matchers.heuristic.gt.GreedySubtreeMatcher;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
public class CompositeMatchers {
|
||||
|
||||
@Register(id = "gumtree", defaultMatcher = true, priority = Registry.Priority.HIGH)
|
||||
public static class ClassicGumtree extends CompositeMatcher {
|
||||
|
||||
public ClassicGumtree(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store, new Matcher[]{
|
||||
new GreedySubtreeMatcher(src, dst, store),
|
||||
new GreedyBottomUpMatcher(src, dst, store)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Register(id = "gumtree-complete")
|
||||
public static class CompleteGumtreeMatcher extends CompositeMatcher {
|
||||
|
||||
public CompleteGumtreeMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store, new Matcher[]{
|
||||
new CliqueSubtreeMatcher(src, dst, store),
|
||||
new CompleteBottomUpMatcher(src, dst, store)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Register(id = "change-distiller")
|
||||
public static class ChangeDistiller extends CompositeMatcher {
|
||||
|
||||
public ChangeDistiller(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store, new Matcher[]{
|
||||
new ChangeDistillerLeavesMatcher(src, dst, store),
|
||||
new ChangeDistillerBottomUpMatcher(src, dst, store)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Register(id = "xy")
|
||||
public static class XyMatcher extends CompositeMatcher {
|
||||
|
||||
public XyMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store, new Matcher[]{
|
||||
new GreedySubtreeMatcher(src, dst, store),
|
||||
new XyBottomUpMatcher(src, dst, store)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.utils.Pair;
|
||||
|
||||
public class Mapping extends Pair<ITree, ITree> {
|
||||
|
||||
public Mapping(ITree a, ITree b) {
|
||||
super(a, b);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
public class MappingStore implements Iterable<Mapping> {
|
||||
|
||||
private Map<ITree, ITree> srcs;
|
||||
private Map<ITree, ITree> dsts;
|
||||
|
||||
public MappingStore(Set<Mapping> mappings) {
|
||||
this();
|
||||
for (Mapping m: mappings) link(m.getFirst(), m.getSecond());
|
||||
}
|
||||
|
||||
public MappingStore() {
|
||||
srcs = new HashMap<>();
|
||||
dsts = new HashMap<>();
|
||||
}
|
||||
|
||||
public Set<Mapping> asSet() {
|
||||
return new AbstractSet<Mapping>() {
|
||||
|
||||
@Override
|
||||
public Iterator<Mapping> iterator() {
|
||||
Iterator<ITree> it = srcs.keySet().iterator();
|
||||
return new Iterator<Mapping>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mapping next() {
|
||||
ITree src = it.next();
|
||||
if (src == null) return null;
|
||||
return new Mapping(src, srcs.get(src));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return srcs.keySet().size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public MappingStore copy() {
|
||||
return new MappingStore(asSet());
|
||||
}
|
||||
|
||||
public void link(ITree src, ITree dst) {
|
||||
srcs.put(src, dst);
|
||||
dsts.put(dst, src);
|
||||
}
|
||||
|
||||
public void unlink(ITree src, ITree dst) {
|
||||
srcs.remove(src);
|
||||
dsts.remove(dst);
|
||||
}
|
||||
|
||||
public ITree firstMappedSrcParent(ITree src) {
|
||||
ITree p = src.getParent();
|
||||
if (p == null) return null;
|
||||
else {
|
||||
while (!hasSrc(p)) {
|
||||
p = p.getParent();
|
||||
if (p == null) return p;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
public ITree firstMappedDstParent(ITree dst) {
|
||||
ITree p = dst.getParent();
|
||||
if (p == null) return null;
|
||||
else {
|
||||
while (!hasDst(p)) {
|
||||
p = p.getParent();
|
||||
if (p == null) return p;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
public ITree getDst(ITree src) {
|
||||
return srcs.get(src);
|
||||
}
|
||||
|
||||
public ITree getSrc(ITree dst) {
|
||||
return dsts.get(dst);
|
||||
}
|
||||
|
||||
public boolean hasSrc(ITree src) {
|
||||
return srcs.containsKey(src);
|
||||
}
|
||||
|
||||
public boolean hasDst(ITree dst) {
|
||||
return dsts.containsKey(dst);
|
||||
}
|
||||
|
||||
public boolean has(ITree src, ITree dst) {
|
||||
return srcs.get(src) == dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether or not a tree is mappable to another given tree.
|
||||
* @return true if both trees are not mapped and if the trees have the same type, false either.
|
||||
*/
|
||||
public boolean isMatchable(ITree src, ITree dst) {
|
||||
return src.hasSameType(dst) && !(srcs.containsKey(src) || dsts.containsKey(dst));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Mapping> iterator() {
|
||||
return asSet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return asSet().toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public abstract class Matcher {
|
||||
|
||||
public static final Logger LOGGER = Logger.getLogger("com.github.gumtreediff.matchers");
|
||||
|
||||
protected final ITree src;
|
||||
|
||||
protected final ITree dst;
|
||||
|
||||
protected final MappingStore mappings;
|
||||
|
||||
public Matcher(ITree src, ITree dst, MappingStore mappings) {
|
||||
this.src = src;
|
||||
this.dst = dst;
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
public abstract void match();
|
||||
|
||||
public MappingStore getMappings() {
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public Set<Mapping> getMappingSet() {
|
||||
return mappings.asSet();
|
||||
}
|
||||
|
||||
public ITree getSrc() {
|
||||
return src;
|
||||
}
|
||||
|
||||
public ITree getDst() {
|
||||
return dst;
|
||||
}
|
||||
|
||||
protected void addMapping(ITree src, ITree dst) {
|
||||
mappings.link(src, dst);
|
||||
}
|
||||
|
||||
protected void addMappingRecursively(ITree src, ITree dst) {
|
||||
List<ITree> srcTrees = src.getTrees();
|
||||
List<ITree> dstTrees = dst.getTrees();
|
||||
for (int i = 0; i < srcTrees.size(); i++) {
|
||||
ITree currentSrcTree = srcTrees.get(i);
|
||||
ITree currentDstTree = dstTrees.get(i);
|
||||
addMapping(currentSrcTree, currentDstTree);
|
||||
}
|
||||
}
|
||||
|
||||
public double chawatheSimilarity(ITree src, ITree dst) {
|
||||
int max = Math.max(src.getDescendants().size(), dst.getDescendants().size());
|
||||
return (double) numberOfCommonDescendants(src, dst) / (double) max;
|
||||
}
|
||||
|
||||
public double diceSimilarity(ITree src, ITree dst) {
|
||||
double c = (double) numberOfCommonDescendants(src, dst);
|
||||
return (2D * c) / ((double) src.getDescendants().size() + (double) dst.getDescendants().size());
|
||||
}
|
||||
|
||||
public double jaccardSimilarity(ITree src, ITree dst) {
|
||||
double num = (double) numberOfCommonDescendants(src, dst);
|
||||
double den = (double) src.getDescendants().size() + (double) dst.getDescendants().size() - num;
|
||||
return num / den;
|
||||
}
|
||||
|
||||
protected int numberOfCommonDescendants(ITree src, ITree dst) {
|
||||
Set<ITree> dstDescandants = new HashSet<>(dst.getDescendants());
|
||||
int common = 0;
|
||||
|
||||
for (ITree t : src.getDescendants()) {
|
||||
ITree m = mappings.getDst(t);
|
||||
if (m != null && dstDescandants.contains(m))
|
||||
common++;
|
||||
}
|
||||
|
||||
return common;
|
||||
}
|
||||
|
||||
public boolean isMappingAllowed(ITree src, ITree dst) {
|
||||
return src.hasSameType(dst) && !(mappings.hasSrc(src) || mappings.hasDst(dst));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers;
|
||||
|
||||
import com.github.gumtreediff.gen.Registry;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
public class Matchers extends Registry<String, Matcher, Register> {
|
||||
|
||||
private static Matchers registry;
|
||||
private Factory<? extends Matcher> defaultMatcherFactory; // FIXME shouln't be removed and use priority instead ?
|
||||
|
||||
public static Matchers getInstance() {
|
||||
if (registry == null)
|
||||
registry = new Matchers();
|
||||
return registry;
|
||||
}
|
||||
|
||||
private Matchers() {
|
||||
install(CompositeMatchers.ClassicGumtree.class);
|
||||
install(CompositeMatchers.ChangeDistiller.class);
|
||||
install(CompositeMatchers.XyMatcher.class);
|
||||
}
|
||||
|
||||
private void install(Class<? extends Matcher> clazz) {
|
||||
Register a = clazz.getAnnotation(Register.class);
|
||||
if (a == null)
|
||||
throw new RuntimeException("Expecting @Register annotation on " + clazz.getName());
|
||||
if (defaultMatcherFactory == null && a.defaultMatcher())
|
||||
defaultMatcherFactory = defaultFactory(clazz, ITree.class, ITree.class, MappingStore.class);
|
||||
install(clazz, a);
|
||||
}
|
||||
|
||||
public Matcher getMatcher(String id, ITree src, ITree dst) {
|
||||
return get(id, src, dst, new MappingStore());
|
||||
}
|
||||
|
||||
public Matcher getMatcher(ITree src, ITree dst) {
|
||||
return defaultMatcherFactory.instantiate(new Object[]{src, dst, new MappingStore()});
|
||||
}
|
||||
|
||||
protected String getName(Register annotation, Class<? extends Matcher> clazz) {
|
||||
return annotation.id();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Entry newEntry(Class<? extends Matcher> clazz, Register annotation) {
|
||||
return new Entry(annotation.id(), clazz,
|
||||
defaultFactory(clazz, ITree.class, ITree.class, MappingStore.class), annotation.priority()) {
|
||||
|
||||
@Override
|
||||
protected boolean handle(String key) {
|
||||
return annotation.id().equals(key); // Fixme remove
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
public class MultiMappingStore implements Iterable<Mapping> {
|
||||
|
||||
private Map<ITree, Set<ITree>> srcs;
|
||||
|
||||
private Map<ITree, Set<ITree>> dsts;
|
||||
|
||||
public MultiMappingStore(Set<Mapping> mappings) {
|
||||
this();
|
||||
for (Mapping m: mappings) link(m.getFirst(), m.getSecond());
|
||||
}
|
||||
|
||||
public MultiMappingStore() {
|
||||
srcs = new HashMap<>();
|
||||
dsts = new HashMap<>();
|
||||
}
|
||||
|
||||
public Set<Mapping> getMappings() {
|
||||
Set<Mapping> mappings = new HashSet<>();
|
||||
for (ITree src : srcs.keySet())
|
||||
for (ITree dst: srcs.get(src))
|
||||
mappings.add(new Mapping(src, dst));
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public void link(ITree src, ITree dst) {
|
||||
if (!srcs.containsKey(src)) srcs.put(src, new HashSet<ITree>());
|
||||
srcs.get(src).add(dst);
|
||||
if (!dsts.containsKey(dst)) dsts.put(dst, new HashSet<ITree>());
|
||||
dsts.get(dst).add(src);
|
||||
}
|
||||
|
||||
public void unlink(ITree src, ITree dst) {
|
||||
srcs.get(src).remove(dst);
|
||||
dsts.get(dst).remove(src);
|
||||
}
|
||||
|
||||
public Set<ITree> getDst(ITree src) {
|
||||
return srcs.get(src);
|
||||
}
|
||||
|
||||
public Set<ITree> getSrcs() {
|
||||
return srcs.keySet();
|
||||
}
|
||||
|
||||
public Set<ITree> getDsts() {
|
||||
return dsts.keySet();
|
||||
}
|
||||
|
||||
public Set<ITree> getSrc(ITree dst) {
|
||||
return dsts.get(dst);
|
||||
}
|
||||
|
||||
public boolean hasSrc(ITree src) {
|
||||
return srcs.containsKey(src);
|
||||
}
|
||||
|
||||
public boolean hasDst(ITree dst) {
|
||||
return dsts.containsKey(dst);
|
||||
}
|
||||
|
||||
public boolean has(ITree src, ITree dst) {
|
||||
return srcs.get(src).contains(dst);
|
||||
}
|
||||
|
||||
public boolean isSrcUnique(ITree src) {
|
||||
return srcs.get(src).size() == 1 && dsts.get(srcs.get(src).iterator().next()).size() == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Mapping> iterator() {
|
||||
return getMappings().iterator();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers;
|
||||
|
||||
import com.github.gumtreediff.gen.Registry;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Register {
|
||||
String id();
|
||||
|
||||
int priority() default Registry.Priority.MEDIUM;
|
||||
|
||||
boolean defaultMatcher() default false;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic;
|
||||
|
||||
import com.github.gumtreediff.utils.StringAlgorithms;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.matchers.Register;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Register(id = "lcs")
|
||||
public class LcsMatcher extends Matcher {
|
||||
|
||||
public LcsMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void match() {
|
||||
List<ITree> srcSeq = TreeUtils.preOrder(src);
|
||||
List<ITree> dstSeq = TreeUtils.preOrder(dst);
|
||||
List<int[]> lcs = StringAlgorithms.lcss(srcSeq, dstSeq);
|
||||
System.out.println(lcs.size());
|
||||
for (int[] x: lcs) {
|
||||
|
||||
ITree t1 = srcSeq.get(x[0]);
|
||||
ITree t2 = dstSeq.get(x[1]);
|
||||
addMapping(t1, t2);
|
||||
}
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic;
|
||||
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* Match the nodes using a bottom-up approach. It browse the nodes of the source and destination trees
|
||||
* using a post-order traversal, testing if the two selected trees might be mapped. The two trees are mapped
|
||||
* if they are mappable and have a dice coefficient greater than SIM_THRESHOLD. Whenever two trees are mapped
|
||||
* a exact ZS algorithm is applied to look to possibly forgotten nodes.
|
||||
*/
|
||||
public class XyBottomUpMatcher extends Matcher {
|
||||
|
||||
private static final double SIM_THRESHOLD = Double.parseDouble(System.getProperty("gumtree.match.xy.sim", "0.5"));
|
||||
|
||||
public XyBottomUpMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store);
|
||||
}
|
||||
|
||||
public void match() {
|
||||
for (ITree src: this.src.postOrder()) {
|
||||
if (src.isRoot()) {
|
||||
addMapping(src, this.dst);
|
||||
lastChanceMatch(src, this.dst);
|
||||
} else if (!(mappings.hasSrc(src) || src.isLeaf())) {
|
||||
Set<ITree> candidates = getDstCandidates(src);
|
||||
ITree best = null;
|
||||
double max = -1D;
|
||||
|
||||
for (ITree cand: candidates ) {
|
||||
double sim = jaccardSimilarity(src, cand);
|
||||
if (sim > max && sim >= SIM_THRESHOLD) {
|
||||
max = sim;
|
||||
best = cand;
|
||||
}
|
||||
}
|
||||
|
||||
if (best != null) {
|
||||
lastChanceMatch(src, best);
|
||||
addMapping(src, best);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Set<ITree> getDstCandidates(ITree src) {
|
||||
Set<ITree> seeds = new HashSet<>();
|
||||
for (ITree c: src.getDescendants()) {
|
||||
ITree m = mappings.getDst(c);
|
||||
if (m != null) seeds.add(m);
|
||||
}
|
||||
Set<ITree> candidates = new HashSet<>();
|
||||
Set<ITree> visited = new HashSet<>();
|
||||
for (ITree seed: seeds) {
|
||||
while (seed.getParent() != null) {
|
||||
ITree parent = seed.getParent();
|
||||
if (visited.contains(parent))
|
||||
break;
|
||||
visited.add(parent);
|
||||
if (parent.getType() == src.getType() && !mappings.hasDst(parent))
|
||||
candidates.add(parent);
|
||||
seed = parent;
|
||||
}
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
private void lastChanceMatch(ITree src, ITree dst) {
|
||||
Map<Integer,List<ITree>> srcKinds = new HashMap<>();
|
||||
Map<Integer,List<ITree>> dstKinds = new HashMap<>();
|
||||
for (ITree c: src.getChildren()) {
|
||||
if (!srcKinds.containsKey(c.getType())) srcKinds.put(c.getType(), new ArrayList<>());
|
||||
srcKinds.get(c.getType()).add(c);
|
||||
}
|
||||
for (ITree c: dst.getChildren()) {
|
||||
if (!dstKinds.containsKey(c.getType())) dstKinds.put(c.getType(), new ArrayList<>());
|
||||
dstKinds.get(c.getType()).add(c);
|
||||
}
|
||||
|
||||
for (int t: srcKinds.keySet())
|
||||
if (dstKinds.get(t) != null && srcKinds.get(t).size() == dstKinds.get(t).size() && srcKinds.get(t).size() == 1)
|
||||
addMapping(srcKinds.get(t).get(0), dstKinds.get(t).get(0));
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic.cd;
|
||||
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ChangeDistillerBottomUpMatcher extends Matcher {
|
||||
|
||||
public static final double STRUCT_SIM_THRESHOLD_1 = 0.6D;
|
||||
|
||||
public static final double STRUCT_SIM_THRESHOLD_2 = 0.4D;
|
||||
|
||||
public ChangeDistillerBottomUpMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void match() {
|
||||
List<ITree> poDst = TreeUtils.postOrder(dst);
|
||||
for (ITree src: this.src.postOrder()) {
|
||||
int l = numberOfLeafs(src);
|
||||
for (ITree dst: poDst) {
|
||||
if (isMappingAllowed(src, dst) && !(src.isLeaf() || dst.isLeaf())) {
|
||||
double sim = chawatheSimilarity(src, dst);
|
||||
if ((l > 4 && sim >= STRUCT_SIM_THRESHOLD_1) || (l <= 4 && sim >= STRUCT_SIM_THRESHOLD_2)) {
|
||||
addMapping(src, dst);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int numberOfLeafs(ITree root) {
|
||||
int l = 0;
|
||||
for (ITree t : root.getDescendants())
|
||||
if (t.isLeaf())
|
||||
l++;
|
||||
return l;
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic.cd;
|
||||
|
||||
import com.github.gumtreediff.matchers.Mapping;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeUtils;
|
||||
import org.simmetrics.StringMetrics;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ChangeDistillerLeavesMatcher extends Matcher {
|
||||
|
||||
public static final double LABEL_SIM_THRESHOLD = 0.5D;
|
||||
|
||||
public ChangeDistillerLeavesMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void match() {
|
||||
List<ITree> dstLeaves = retainLeaves(TreeUtils.postOrder(dst));
|
||||
|
||||
List<Mapping> leafMappings = new LinkedList<>();
|
||||
|
||||
for (Iterator<ITree> srcLeaves = TreeUtils.leafIterator(
|
||||
TreeUtils.postOrderIterator(src)); srcLeaves.hasNext();) {
|
||||
for (ITree dstLeaf: dstLeaves) {
|
||||
ITree srcLeaf = srcLeaves.next();
|
||||
if (isMappingAllowed(srcLeaf, dstLeaf)) {
|
||||
double sim = StringMetrics.qGramsDistance().compare(srcLeaf.getLabel(), dstLeaf.getLabel());
|
||||
if (sim > LABEL_SIM_THRESHOLD) leafMappings.add(new Mapping(srcLeaf, dstLeaf));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<ITree> srcIgnored = new HashSet<>();
|
||||
Set<ITree> dstIgnored = new HashSet<>();
|
||||
Collections.sort(leafMappings, new LeafMappingComparator());
|
||||
while (leafMappings.size() > 0) {
|
||||
Mapping best = leafMappings.remove(0);
|
||||
if (!(srcIgnored.contains(best.getFirst()) || dstIgnored.contains(best.getSecond()))) {
|
||||
addMapping(best.getFirst(),best.getSecond());
|
||||
srcIgnored.add(best.getFirst());
|
||||
dstIgnored.add(best.getSecond());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<ITree> retainLeaves(List<ITree> trees) {
|
||||
Iterator<ITree> tIt = trees.iterator();
|
||||
while (tIt.hasNext()) {
|
||||
ITree t = tIt.next();
|
||||
if (!t.isLeaf()) tIt.remove();
|
||||
}
|
||||
return trees;
|
||||
}
|
||||
|
||||
private class LeafMappingComparator implements Comparator<Mapping> {
|
||||
|
||||
@Override
|
||||
public int compare(Mapping m1, Mapping m2) {
|
||||
return Double.compare(sim(m1), sim(m2));
|
||||
}
|
||||
|
||||
public double sim(Mapping m) {
|
||||
|
||||
return StringMetrics.qGramsDistance().compare(m.getFirst().getLabel(), m.getSecond().getLabel());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2016 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2016 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic.gt;
|
||||
|
||||
import com.github.gumtreediff.matchers.Mapping;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.matchers.optimal.zs.ZsMatcher;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class AbstractBottomUpMatcher extends Matcher {
|
||||
public static int SIZE_THRESHOLD =
|
||||
Integer.parseInt(System.getProperty("gt.bum.szt", "1000"));
|
||||
public static final double SIM_THRESHOLD =
|
||||
Double.parseDouble(System.getProperty("gt.bum.smt", "0.5"));
|
||||
|
||||
protected TreeMap srcIds;
|
||||
protected TreeMap dstIds;
|
||||
|
||||
protected TreeMap mappedSrc;
|
||||
protected TreeMap mappedDst;
|
||||
|
||||
public AbstractBottomUpMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store);
|
||||
srcIds = new TreeMap(src);
|
||||
dstIds = new TreeMap(dst);
|
||||
|
||||
mappedSrc = new TreeMap();
|
||||
mappedDst = new TreeMap();
|
||||
for (Mapping m : store.asSet()) {
|
||||
mappedSrc.putTrees(m.getFirst());
|
||||
mappedDst.putTrees(m.getSecond());
|
||||
}
|
||||
}
|
||||
|
||||
protected List<ITree> getDstCandidates(ITree src) {
|
||||
List<ITree> seeds = new ArrayList<>();
|
||||
for (ITree c: src.getDescendants()) {
|
||||
ITree m = mappings.getDst(c);
|
||||
if (m != null) seeds.add(m);
|
||||
}
|
||||
List<ITree> candidates = new ArrayList<>();
|
||||
Set<ITree> visited = new HashSet<>();
|
||||
for (ITree seed: seeds) {
|
||||
while (seed.getParent() != null) {
|
||||
ITree parent = seed.getParent();
|
||||
if (visited.contains(parent))
|
||||
break;
|
||||
visited.add(parent);
|
||||
if (parent.getType() == src.getType() && !isDstMatched(parent) && !parent.isRoot())
|
||||
candidates.add(parent);
|
||||
seed = parent;
|
||||
}
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
//FIXME checks if it is better or not to remove the already found mappings.
|
||||
protected void lastChanceMatch(ITree src, ITree dst) {
|
||||
ITree cSrc = src.deepCopy();
|
||||
ITree cDst = dst.deepCopy();
|
||||
removeMatched(cSrc, true);
|
||||
removeMatched(cDst, false);
|
||||
|
||||
if (cSrc.getSize() < AbstractBottomUpMatcher.SIZE_THRESHOLD
|
||||
|| cDst.getSize() < AbstractBottomUpMatcher.SIZE_THRESHOLD) {
|
||||
Matcher m = new ZsMatcher(cSrc, cDst, new MappingStore());
|
||||
m.match();
|
||||
for (Mapping candidate: m.getMappings()) {
|
||||
ITree left = srcIds.getTree(candidate.getFirst().getId());
|
||||
ITree right = dstIds.getTree(candidate.getSecond().getId());
|
||||
|
||||
if (left.getId() == src.getId() || right.getId() == dst.getId()) {
|
||||
// System.err.printf("Trying to map already mapped source node (%d == %d || %d == %d)\n",
|
||||
// left.getId(), src.getId(), right.getId(), dst.getId());
|
||||
continue;
|
||||
} else if (!isMappingAllowed(left, right)) {
|
||||
// System.err.printf("Trying to map incompatible nodes (%s, %s)\n",
|
||||
// left.toShortString(), right.toShortString());
|
||||
continue;
|
||||
} else if (!left.getParent().hasSameType(right.getParent())) {
|
||||
// System.err.printf("Trying to map nodes with incompatible parents (%s, %s)\n",
|
||||
// left.getParent().toShortString(), right.getParent().toShortString());
|
||||
continue;
|
||||
} else
|
||||
addMapping(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
mappedSrc.putTrees(src);
|
||||
mappedDst.putTrees(dst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove mapped nodes from the tree. Be careful this method will invalidate
|
||||
* all the metrics of this tree and its descendants. If you need them, you need
|
||||
* to recompute them.
|
||||
*/
|
||||
public ITree removeMatched(ITree tree, boolean isSrc) {
|
||||
for (ITree t: tree.getTrees()) {
|
||||
if ((isSrc && isSrcMatched(t)) || ((!isSrc) && isDstMatched(t))) {
|
||||
if (t.getParent() != null) t.getParent().getChildren().remove(t);
|
||||
t.setParent(null);
|
||||
}
|
||||
}
|
||||
tree.refresh();
|
||||
return tree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMappingAllowed(ITree src, ITree dst) {
|
||||
return src.hasSameType(dst)
|
||||
&& !(isSrcMatched(src) || isDstMatched(dst));
|
||||
}
|
||||
|
||||
protected void addMapping(ITree src, ITree dst) {
|
||||
mappedSrc.putTree(src);
|
||||
mappedDst.putTree(dst);
|
||||
super.addMapping(src, dst);
|
||||
}
|
||||
|
||||
boolean isSrcMatched(ITree tree) {
|
||||
return mappedSrc.contains(tree);
|
||||
}
|
||||
|
||||
boolean isDstMatched(ITree tree) {
|
||||
return mappedDst.contains(tree);
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2016 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic.gt;
|
||||
|
||||
import com.github.gumtreediff.matchers.Mapping;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class AbstractMappingComparator implements Comparator<Mapping> {
|
||||
|
||||
protected List<Mapping> ambiguousMappings;
|
||||
|
||||
protected Map<Mapping, Double> similarities = new HashMap<>();
|
||||
|
||||
protected int maxTreeSize;
|
||||
|
||||
protected MappingStore mappings;
|
||||
|
||||
public AbstractMappingComparator(List<Mapping> ambiguousMappings, MappingStore mappings, int maxTreeSize) {
|
||||
this.maxTreeSize = maxTreeSize;
|
||||
this.mappings = mappings;
|
||||
this.ambiguousMappings = ambiguousMappings;
|
||||
}
|
||||
|
||||
public int compare(Mapping m1, Mapping m2) {
|
||||
return Double.compare(similarities.get(m2), similarities.get(m1));
|
||||
}
|
||||
|
||||
protected abstract double similarity(ITree src, ITree dst);
|
||||
|
||||
protected double posInParentSimilarity(ITree src, ITree dst) {
|
||||
int posSrc = (src.isRoot()) ? 0 : src.getParent().getChildPosition(src);
|
||||
int posDst = (dst.isRoot()) ? 0 : dst.getParent().getChildPosition(dst);
|
||||
int maxSrcPos = (src.isRoot()) ? 1 : src.getParent().getChildren().size();
|
||||
int maxDstPos = (dst.isRoot()) ? 1 : dst.getParent().getChildren().size();
|
||||
int maxPosDiff = Math.max(maxSrcPos, maxDstPos);
|
||||
return 1D - ((double) Math.abs(posSrc - posDst) / (double) maxPosDiff);
|
||||
}
|
||||
|
||||
protected double numberingSimilarity(ITree src, ITree dst) {
|
||||
return 1D - ((double) Math.abs(src.getId() - dst.getId())
|
||||
/ (double) maxTreeSize);
|
||||
}
|
||||
|
||||
}
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic.gt;
|
||||
|
||||
import com.github.gumtreediff.matchers.*;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.MultiMappingStore;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class AbstractSubtreeMatcher extends Matcher {
|
||||
|
||||
public static int MIN_HEIGHT = Integer.parseInt(System.getProperty("gt.stm.mh", "2"));
|
||||
|
||||
public AbstractSubtreeMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store);
|
||||
}
|
||||
|
||||
private void popLarger(PriorityTreeList srcTrees, PriorityTreeList dstTrees) {
|
||||
if (srcTrees.peekHeight() > dstTrees.peekHeight())
|
||||
srcTrees.open();
|
||||
else
|
||||
dstTrees.open();
|
||||
}
|
||||
|
||||
public void match() {
|
||||
MultiMappingStore multiMappings = new MultiMappingStore();
|
||||
|
||||
PriorityTreeList srcTrees = new PriorityTreeList(src);
|
||||
PriorityTreeList dstTrees = new PriorityTreeList(dst);
|
||||
|
||||
while (srcTrees.peekHeight() != -1 && dstTrees.peekHeight() != -1) {
|
||||
while (srcTrees.peekHeight() != dstTrees.peekHeight())
|
||||
popLarger(srcTrees, dstTrees);
|
||||
|
||||
List<ITree> currentHeightSrcTrees = srcTrees.pop();
|
||||
List<ITree> currentHeightDstTrees = dstTrees.pop();
|
||||
|
||||
boolean[] marksForSrcTrees = new boolean[currentHeightSrcTrees.size()];
|
||||
boolean[] marksForDstTrees = new boolean[currentHeightDstTrees.size()];
|
||||
|
||||
for (int i = 0; i < currentHeightSrcTrees.size(); i++) {
|
||||
for (int j = 0; j < currentHeightDstTrees.size(); j++) {
|
||||
ITree src = currentHeightSrcTrees.get(i);
|
||||
ITree dst = currentHeightDstTrees.get(j);
|
||||
|
||||
if (src.isIsomorphicTo(dst)) {
|
||||
multiMappings.link(src, dst);
|
||||
marksForSrcTrees[i] = true;
|
||||
marksForDstTrees[j] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < marksForSrcTrees.length; i++)
|
||||
if (marksForSrcTrees[i] == false)
|
||||
srcTrees.open(currentHeightSrcTrees.get(i));
|
||||
for (int j = 0; j < marksForDstTrees.length; j++)
|
||||
if (marksForDstTrees[j] == false)
|
||||
dstTrees.open(currentHeightDstTrees.get(j));
|
||||
srcTrees.updateHeight();
|
||||
dstTrees.updateHeight();
|
||||
}
|
||||
|
||||
filterMappings(multiMappings);
|
||||
}
|
||||
|
||||
public abstract void filterMappings(MultiMappingStore multiMappings);
|
||||
|
||||
protected double sim(ITree src, ITree dst) {
|
||||
double jaccard = jaccardSimilarity(src.getParent(), dst.getParent());
|
||||
int posSrc = (src.isRoot()) ? 0 : src.getParent().getChildPosition(src);
|
||||
int posDst = (dst.isRoot()) ? 0 : dst.getParent().getChildPosition(dst);
|
||||
int maxSrcPos = (src.isRoot()) ? 1 : src.getParent().getChildren().size();
|
||||
int maxDstPos = (dst.isRoot()) ? 1 : dst.getParent().getChildren().size();
|
||||
int maxPosDiff = Math.max(maxSrcPos, maxDstPos);
|
||||
double pos = 1D - ((double) Math.abs(posSrc - posDst) / (double) maxPosDiff);
|
||||
double po = 1D - ((double) Math.abs(src.getId() - dst.getId()) / (double) this.getMaxTreeSize());
|
||||
return 100 * jaccard + 10 * pos + po;
|
||||
}
|
||||
|
||||
protected int getMaxTreeSize() {
|
||||
return Math.max(src.getSize(), dst.getSize());
|
||||
}
|
||||
|
||||
protected void retainBestMapping(List<Mapping> mappings, Set<ITree> srcIgnored, Set<ITree> dstIgnored) {
|
||||
while (mappings.size() > 0) {
|
||||
Mapping mapping = mappings.remove(0);
|
||||
if (!(srcIgnored.contains(mapping.getFirst()) || dstIgnored.contains(mapping.getSecond()))) {
|
||||
addMappingRecursively(mapping.getFirst(), mapping.getSecond());
|
||||
srcIgnored.add(mapping.getFirst());
|
||||
dstIgnored.add(mapping.getSecond());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class PriorityTreeList {
|
||||
|
||||
private List<ITree>[] trees;
|
||||
|
||||
private int maxHeight;
|
||||
|
||||
private int currentIdx;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public PriorityTreeList(ITree tree) {
|
||||
int listSize = tree.getHeight() - MIN_HEIGHT + 1;
|
||||
if (listSize < 0)
|
||||
listSize = 0;
|
||||
if (listSize == 0)
|
||||
currentIdx = -1;
|
||||
trees = (List<ITree>[]) new ArrayList[listSize];
|
||||
maxHeight = tree.getHeight();
|
||||
addTree(tree);
|
||||
}
|
||||
|
||||
private int idx(ITree tree) {
|
||||
return idx(tree.getHeight());
|
||||
}
|
||||
|
||||
private int idx(int height) {
|
||||
return maxHeight - height;
|
||||
}
|
||||
|
||||
private int height(int idx) {
|
||||
return maxHeight - idx;
|
||||
}
|
||||
|
||||
private void addTree(ITree tree) {
|
||||
if (tree.getHeight() >= MIN_HEIGHT) {
|
||||
int idx = idx(tree);
|
||||
if (trees[idx] == null) trees[idx] = new ArrayList<>();
|
||||
trees[idx].add(tree);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ITree> open() {
|
||||
List<ITree> pop = pop();
|
||||
if (pop != null) {
|
||||
for (ITree tree: pop) open(tree);
|
||||
updateHeight();
|
||||
return pop;
|
||||
} else return null;
|
||||
}
|
||||
|
||||
public List<ITree> pop() {
|
||||
if (currentIdx == -1)
|
||||
return null;
|
||||
else {
|
||||
List<ITree> pop = trees[currentIdx];
|
||||
trees[currentIdx] = null;
|
||||
return pop;
|
||||
}
|
||||
}
|
||||
|
||||
public void open(ITree tree) {
|
||||
for (ITree c: tree.getChildren()) addTree(c);
|
||||
}
|
||||
|
||||
public int peekHeight() {
|
||||
return (currentIdx == -1) ? -1 : height(currentIdx);
|
||||
}
|
||||
|
||||
public void updateHeight() {
|
||||
currentIdx = -1;
|
||||
for (int i = 0; i < trees.length; i++) {
|
||||
if (trees[i] != null) {
|
||||
currentIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic.gt;
|
||||
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.MultiMappingStore;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.utils.Pair;
|
||||
import com.github.gumtreediff.matchers.Mapping;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class CliqueSubtreeMatcher extends AbstractSubtreeMatcher {
|
||||
|
||||
public CliqueSubtreeMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filterMappings(MultiMappingStore multiMappings) {
|
||||
TIntObjectHashMap<Pair<List<ITree>, List<ITree>>> cliques = new TIntObjectHashMap<>();
|
||||
for (Mapping m : multiMappings) {
|
||||
int hash = m.getFirst().getHash();
|
||||
if (!cliques.containsKey(hash))
|
||||
cliques.put(hash, new Pair<>(new ArrayList<>(), new ArrayList<>()));
|
||||
cliques.get(hash).getFirst().add(m.getFirst());
|
||||
cliques.get(hash).getSecond().add(m.getSecond());
|
||||
}
|
||||
|
||||
List<Pair<List<ITree>, List<ITree>>> ccliques = new ArrayList<>();
|
||||
|
||||
for (int hash : cliques.keys()) {
|
||||
Pair<List<ITree>, List<ITree>> clique = cliques.get(hash);
|
||||
if (clique.getFirst().size() == 1 && clique.getSecond().size() == 1) {
|
||||
addMappingRecursively(clique.getFirst().get(0), clique.getSecond().get(0));
|
||||
cliques.remove(hash);
|
||||
} else
|
||||
ccliques.add(clique);
|
||||
}
|
||||
|
||||
Collections.sort(ccliques, new CliqueComparator());
|
||||
|
||||
for (Pair<List<ITree>, List<ITree>> clique : ccliques) {
|
||||
List<Mapping> cliqueAsMappings = fromClique(clique);
|
||||
Collections.sort(cliqueAsMappings, new MappingComparator(cliqueAsMappings));
|
||||
Set<ITree> srcIgnored = new HashSet<>();
|
||||
Set<ITree> dstIgnored = new HashSet<>();
|
||||
retainBestMapping(cliqueAsMappings, srcIgnored, dstIgnored);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Mapping> fromClique(Pair<List<ITree>, List<ITree>> clique) {
|
||||
List<Mapping> cliqueAsMappings = new ArrayList<Mapping>();
|
||||
for (ITree src: clique.getFirst())
|
||||
for (ITree dst: clique.getFirst())
|
||||
cliqueAsMappings.add(new Mapping(src, dst));
|
||||
return cliqueAsMappings;
|
||||
}
|
||||
|
||||
private static class CliqueComparator implements Comparator<Pair<List<ITree>, List<ITree>>> {
|
||||
|
||||
@Override
|
||||
public int compare(Pair<List<ITree>, List<ITree>> l1,
|
||||
Pair<List<ITree>, List<ITree>> l2) {
|
||||
int minDepth1 = minDepth(l1);
|
||||
int minDepth2 = minDepth(l2);
|
||||
if (minDepth1 != minDepth2)
|
||||
return -1 * Integer.compare(minDepth1, minDepth2);
|
||||
else {
|
||||
int size1 = size(l1);
|
||||
int size2 = size(l2);
|
||||
return -1 * Integer.compare(size1, size2);
|
||||
}
|
||||
}
|
||||
|
||||
private int minDepth(Pair<List<ITree>, List<ITree>> trees) {
|
||||
int depth = Integer.MAX_VALUE;
|
||||
for (ITree t : trees.getFirst())
|
||||
if (depth > t.getDepth())
|
||||
depth = t.getDepth();
|
||||
for (ITree t : trees.getSecond())
|
||||
if (depth > t.getDepth())
|
||||
depth = t.getDepth();
|
||||
return depth;
|
||||
}
|
||||
|
||||
private int size(Pair<List<ITree>, List<ITree>> trees) {
|
||||
return trees.getFirst().size() + trees.getSecond().size();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class MappingComparator implements Comparator<Mapping> {
|
||||
|
||||
private Map<Mapping, double[]> simMap = new HashMap<>();
|
||||
|
||||
public MappingComparator(List<Mapping> mappings) {
|
||||
for (Mapping mapping: mappings)
|
||||
simMap.put(mapping, sims(mapping.getFirst(), mapping.getSecond()));
|
||||
}
|
||||
|
||||
public int compare(Mapping m1, Mapping m2) {
|
||||
double[] sims1 = simMap.get(m1);
|
||||
double[] sims2 = simMap.get(m2);
|
||||
for (int i = 0; i < sims1.length; i++) {
|
||||
if (sims1[i] != sims2[i])
|
||||
return -1 * Double.compare(sims2[i], sims2[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Map<ITree, List<ITree>> srcDescendants = new HashMap<>();
|
||||
|
||||
private Map<ITree, Set<ITree>> dstDescendants = new HashMap<>();
|
||||
|
||||
protected int numberOfCommonDescendants(ITree src, ITree dst) {
|
||||
if (!srcDescendants.containsKey(src))
|
||||
srcDescendants.put(src, src.getDescendants());
|
||||
if (!dstDescendants.containsKey(dst))
|
||||
dstDescendants.put(dst, new HashSet<>(dst.getDescendants()));
|
||||
|
||||
int common = 0;
|
||||
|
||||
for (ITree t: srcDescendants.get(src)) {
|
||||
ITree m = mappings.getDst(t);
|
||||
if (m != null && dstDescendants.get(dst).contains(m)) common++;
|
||||
}
|
||||
|
||||
return common;
|
||||
}
|
||||
|
||||
protected double[] sims(ITree src, ITree dst) {
|
||||
double[] sims = new double[4];
|
||||
sims[0] = jaccardSimilarity(src.getParent(), dst.getParent());
|
||||
sims[1] = src.positionInParent() - dst.positionInParent();
|
||||
sims[2] = src.getId() - dst.getId();
|
||||
sims[3] = src.getId();
|
||||
return sims;
|
||||
}
|
||||
|
||||
protected double jaccardSimilarity(ITree src, ITree dst) {
|
||||
double num = (double) numberOfCommonDescendants(src, dst);
|
||||
double den = (double) srcDescendants.get(src).size() + (double) dstDescendants.get(dst).size() - num;
|
||||
return num / den;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic.gt;
|
||||
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Match the nodes using a bottom-up approach. It browse the nodes of the source and destination trees
|
||||
* using a post-order traversal, testing if the two selected trees might be mapped. The two trees are mapped
|
||||
* if they are mappable and have a dice coefficient greater than SIM_THRESHOLD. Whenever two trees are mapped
|
||||
* a exact ZS algorithm is applied to look to possibly forgotten nodes.
|
||||
*/
|
||||
public class CompleteBottomUpMatcher extends AbstractBottomUpMatcher {
|
||||
|
||||
public CompleteBottomUpMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store);
|
||||
}
|
||||
|
||||
public void match() {
|
||||
for (ITree t: src.postOrder()) {
|
||||
if (t.isRoot()) {
|
||||
addMapping(t, this.dst);
|
||||
lastChanceMatch(t, this.dst);
|
||||
break;
|
||||
} else if (!(isSrcMatched(t) || t.isLeaf())) {
|
||||
List<ITree> srcCandidates = t.getParents().stream()
|
||||
.filter(p -> p.getType() == t.getType())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<ITree> dstCandidates = getDstCandidates(t);
|
||||
ITree srcBest = null;
|
||||
ITree dstBest = null;
|
||||
double max = -1D;
|
||||
for (ITree srcCand: srcCandidates) {
|
||||
for (ITree dstCand: dstCandidates) {
|
||||
|
||||
double sim = jaccardSimilarity(srcCand, dstCand);
|
||||
if (sim > max && sim >= SIM_THRESHOLD) {
|
||||
max = sim;
|
||||
srcBest = srcCand;
|
||||
dstBest = dstCand;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (srcBest != null) {
|
||||
lastChanceMatch(srcBest, dstBest);
|
||||
addMapping(srcBest, dstBest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic.gt;
|
||||
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
/**
|
||||
* Match the nodes using a bottom-up approach. It browse the nodes of the source and destination trees
|
||||
* using a post-order traversal, testing if the two selected trees might be mapped. The two trees are mapped
|
||||
* if they are mappable and have a dice coefficient greater than SIM_THRESHOLD. Whenever two trees are mapped
|
||||
* a exact ZS algorithm is applied to look to possibly forgotten nodes.
|
||||
*/
|
||||
public class FirstMatchBottomUpMatcher extends AbstractBottomUpMatcher {
|
||||
|
||||
public FirstMatchBottomUpMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store);
|
||||
}
|
||||
|
||||
public void match() {
|
||||
match(removeMatched(src, true), removeMatched(dst, false));
|
||||
}
|
||||
|
||||
private void match(ITree src, ITree dst) {
|
||||
for (ITree s: src.postOrder()) {
|
||||
for (ITree d: dst.postOrder()) {
|
||||
if (isMappingAllowed(s, d) && !(s.isLeaf() || d.isLeaf())) {
|
||||
double sim = jaccardSimilarity(s, d);
|
||||
if (sim >= SIM_THRESHOLD || (s.isRoot() && d.isRoot()) ) {
|
||||
if (!(areDescendantsMatched(s, true) || areDescendantsMatched(d, false)))
|
||||
lastChanceMatch(s, d);
|
||||
addMapping(s, d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether or not all the descendants of the trees are already mapped.
|
||||
*/
|
||||
public boolean areDescendantsMatched(ITree tree, boolean isSrc) {
|
||||
for (ITree c: tree.getDescendants())
|
||||
if (!((isSrc && isSrcMatched(c)) || (!isSrc && isDstMatched(tree)))) // FIXME ugly but this class is unused
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic.gt;
|
||||
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Match the nodes using a bottom-up approach. It browse the nodes of the source and destination trees
|
||||
* using a post-order traversal, testing if the two selected trees might be mapped. The two trees are mapped
|
||||
* if they are mappable and have a dice coefficient greater than SIM_THRESHOLD. Whenever two trees are mapped
|
||||
* a exact ZS algorithm is applied to look to possibly forgotten nodes.
|
||||
*/
|
||||
public class GreedyBottomUpMatcher extends AbstractBottomUpMatcher {
|
||||
|
||||
public static double SIM_THRESHOLD = Double.parseDouble(System.getProperty("gumtree.match.bu.sim", "0.3"));
|
||||
|
||||
public GreedyBottomUpMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store);
|
||||
}
|
||||
|
||||
public void match() {
|
||||
for (ITree t: src.postOrder()) {
|
||||
if (t.isRoot()) {
|
||||
addMapping(t, this.dst);
|
||||
lastChanceMatch(t, this.dst);
|
||||
break;
|
||||
} else if (!(isSrcMatched(t) || t.isLeaf())) {
|
||||
List<ITree> candidates = getDstCandidates(t);
|
||||
ITree best = null;
|
||||
double max = -1D;
|
||||
|
||||
for (ITree cand: candidates) {
|
||||
double sim = jaccardSimilarity(t, cand);
|
||||
if (sim > max && sim >= SIM_THRESHOLD) {
|
||||
max = sim;
|
||||
best = cand;
|
||||
}
|
||||
}
|
||||
|
||||
if (best != null) {
|
||||
lastChanceMatch(t, best);
|
||||
addMapping(t, best);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic.gt;
|
||||
|
||||
import com.github.gumtreediff.matchers.Mapping;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.MultiMappingStore;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class GreedySubtreeMatcher extends AbstractSubtreeMatcher {
|
||||
|
||||
public GreedySubtreeMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store);
|
||||
}
|
||||
|
||||
public void filterMappings(MultiMappingStore multiMappings) {
|
||||
// Select unique mappings first and extract ambiguous mappings.
|
||||
List<Mapping> ambiguousList = new LinkedList<>();
|
||||
Set<ITree> ignored = new HashSet<>();
|
||||
for (ITree src: multiMappings.getSrcs()) {
|
||||
if (multiMappings.isSrcUnique(src))
|
||||
addMappingRecursively(src, multiMappings.getDst(src).iterator().next());
|
||||
else if (!ignored.contains(src)) {
|
||||
Set<ITree> adsts = multiMappings.getDst(src);
|
||||
Set<ITree> asrcs = multiMappings.getSrc(multiMappings.getDst(src).iterator().next());
|
||||
for (ITree asrc : asrcs)
|
||||
for (ITree adst: adsts)
|
||||
ambiguousList.add(new Mapping(asrc, adst));
|
||||
ignored.addAll(asrcs);
|
||||
}
|
||||
}
|
||||
|
||||
// Rank the mappings by score.
|
||||
Set<ITree> srcIgnored = new HashSet<>();
|
||||
Set<ITree> dstIgnored = new HashSet<>();
|
||||
Collections.sort(ambiguousList, new SiblingsMappingComparator(ambiguousList, mappings, getMaxTreeSize()));
|
||||
|
||||
// Select the best ambiguous mappings
|
||||
retainBestMapping(ambiguousList, srcIgnored, dstIgnored);
|
||||
}
|
||||
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic.gt;
|
||||
|
||||
import com.github.gumtreediff.utils.HungarianAlgorithm;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.MultiMappingStore;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class HungarianSubtreeMatcher extends AbstractSubtreeMatcher {
|
||||
|
||||
public HungarianSubtreeMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store);
|
||||
}
|
||||
|
||||
public void filterMappings(MultiMappingStore multiMappings) {
|
||||
List<MultiMappingStore> ambiguousList = new ArrayList<>();
|
||||
Set<ITree> ignored = new HashSet<>();
|
||||
for (ITree src: multiMappings.getSrcs())
|
||||
if (multiMappings.isSrcUnique(src))
|
||||
addMappingRecursively(src, multiMappings.getDst(src).iterator().next());
|
||||
else if (!ignored.contains(src)) {
|
||||
MultiMappingStore ambiguous = new MultiMappingStore();
|
||||
Set<ITree> adsts = multiMappings.getDst(src);
|
||||
Set<ITree> asrcs = multiMappings.getSrc(multiMappings.getDst(src).iterator().next());
|
||||
for (ITree asrc : asrcs)
|
||||
for (ITree adst: adsts)
|
||||
ambiguous.link(asrc ,adst);
|
||||
ambiguousList.add(ambiguous);
|
||||
ignored.addAll(asrcs);
|
||||
}
|
||||
|
||||
Collections.sort(ambiguousList, new MultiMappingComparator());
|
||||
|
||||
for (MultiMappingStore ambiguous: ambiguousList) {
|
||||
System.out.println("hungarian try.");
|
||||
List<ITree> lstSrcs = new ArrayList<>(ambiguous.getSrcs());
|
||||
List<ITree> lstDsts = new ArrayList<>(ambiguous.getDsts());
|
||||
double[][] matrix = new double[lstSrcs.size()][lstDsts.size()];
|
||||
for (int i = 0; i < lstSrcs.size(); i++)
|
||||
for (int j = 0; j < lstDsts.size(); j++)
|
||||
matrix[i][j] = cost(lstSrcs.get(i), lstDsts.get(j));
|
||||
|
||||
HungarianAlgorithm hgAlg = new HungarianAlgorithm(matrix);
|
||||
int[] solutions = hgAlg.execute();
|
||||
for (int i = 0; i < solutions.length; i++) {
|
||||
int dstIdx = solutions[i];
|
||||
if (dstIdx != -1) addMappingRecursively(lstSrcs.get(i), lstDsts.get(dstIdx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double cost(ITree src, ITree dst) {
|
||||
return 111D - sim(src, dst);
|
||||
}
|
||||
|
||||
private class MultiMappingComparator implements Comparator<MultiMappingStore> {
|
||||
|
||||
@Override
|
||||
public int compare(MultiMappingStore m1, MultiMappingStore m2) {
|
||||
return Integer.compare(impact(m1), impact(m2));
|
||||
}
|
||||
|
||||
public int impact(MultiMappingStore m) {
|
||||
int impact = 0;
|
||||
for (ITree src: m.getSrcs()) {
|
||||
int pSize = src.getParents().size();
|
||||
if (pSize > impact) impact = pSize;
|
||||
}
|
||||
for (ITree src: m.getDsts()) {
|
||||
int pSize = src.getParents().size();
|
||||
if (pSize > impact) impact = pSize;
|
||||
}
|
||||
return impact;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2016 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic.gt;
|
||||
|
||||
import com.github.gumtreediff.matchers.Mapping;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.utils.StringAlgorithms;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public final class ParentsMappingComparator extends AbstractMappingComparator {
|
||||
|
||||
public ParentsMappingComparator(List<Mapping> ambiguousMappings, MappingStore mappings, int maxTreeSize) {
|
||||
super(ambiguousMappings, mappings, maxTreeSize);
|
||||
for (Mapping ambiguousMapping: ambiguousMappings)
|
||||
similarities.put(ambiguousMapping, similarity(ambiguousMapping.getFirst(), ambiguousMapping.getSecond()));
|
||||
}
|
||||
|
||||
protected double similarity(ITree src, ITree dst) {
|
||||
return 100D * parentsJaccardSimilarity(src, dst)
|
||||
+ 10D * posInParentSimilarity(src, dst) + numberingSimilarity(src , dst);
|
||||
}
|
||||
|
||||
protected double parentsJaccardSimilarity(ITree src, ITree dst) {
|
||||
List<ITree> srcParents = src.getParents();
|
||||
List<ITree> dstParents = dst.getParents();
|
||||
double numerator = (double) StringAlgorithms.lcss(srcParents, dstParents).size();
|
||||
double denominator = (double) srcParents.size() + (double) dstParents.size() - numerator;
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2016 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.heuristic.gt;
|
||||
|
||||
import com.github.gumtreediff.matchers.Mapping;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public final class SiblingsMappingComparator extends AbstractMappingComparator {
|
||||
|
||||
private Map<ITree, List<ITree>> srcDescendants = new HashMap<>();
|
||||
|
||||
private Map<ITree, Set<ITree>> dstDescendants = new HashMap<>();
|
||||
|
||||
public SiblingsMappingComparator(List<Mapping> ambiguousMappings, MappingStore mappings, int maxTreeSize) {
|
||||
super(ambiguousMappings, mappings, maxTreeSize);
|
||||
for (Mapping ambiguousMapping: ambiguousMappings)
|
||||
similarities.put(ambiguousMapping, similarity(ambiguousMapping.getFirst(), ambiguousMapping.getSecond()));
|
||||
}
|
||||
|
||||
protected double similarity(ITree src, ITree dst) {
|
||||
return 100D * siblingsJaccardSimilarity(src.getParent(), dst.getParent())
|
||||
+ 10D * posInParentSimilarity(src, dst) + numberingSimilarity(src , dst);
|
||||
}
|
||||
|
||||
protected double siblingsJaccardSimilarity(ITree src, ITree dst) {
|
||||
double num = (double) numberOfCommonDescendants(src, dst);
|
||||
double den = (double) srcDescendants.get(src).size() + (double) dstDescendants.get(dst).size() - num;
|
||||
return num / den;
|
||||
}
|
||||
|
||||
protected int numberOfCommonDescendants(ITree src, ITree dst) {
|
||||
if (!srcDescendants.containsKey(src))
|
||||
srcDescendants.put(src, src.getDescendants());
|
||||
if (!dstDescendants.containsKey(dst))
|
||||
dstDescendants.put(dst, new HashSet<>(dst.getDescendants()));
|
||||
|
||||
int common = 0;
|
||||
|
||||
for (ITree t: srcDescendants.get(src)) {
|
||||
ITree m = mappings.getDst(t);
|
||||
if (m != null && dstDescendants.get(dst).contains(m))
|
||||
common++;
|
||||
}
|
||||
|
||||
return common;
|
||||
}
|
||||
|
||||
}
|
||||
+436
@@ -0,0 +1,436 @@
|
||||
// Copyright (C) 2012 Mateusz Pawlik and Nikolaus Augsten
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package com.github.gumtreediff.matchers.optimal.rted;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* Stores all needed information about a single tree in several indeces.
|
||||
*
|
||||
* @author Mateusz Pawlik
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public class InfoTree {
|
||||
|
||||
private ITree inputTree;
|
||||
|
||||
private static final byte LEFT = 0;
|
||||
private static final byte RIGHT = 1;
|
||||
private static final byte HEAVY = 2;
|
||||
private static final byte BOTH = 3;
|
||||
//private static final byte REVLEFT = 4;
|
||||
//private static final byte REVRIGHT = 5;
|
||||
//private static final byte REVHEAVY = 6;
|
||||
|
||||
// constants for indeces numbers
|
||||
public static final byte POST2_SIZE = 0;
|
||||
public static final byte POST2_KR_SUM = 1;
|
||||
public static final byte POST2_REV_KR_SUM = 2;
|
||||
public static final byte POST2_DESC_SUM = 3; // number of subforests in full decomposition
|
||||
public static final byte POST2_PRE = 4;
|
||||
public static final byte POST2_PARENT = 5;
|
||||
public static final byte POST2_LABEL = 6;
|
||||
public static final byte KR = 7; // key root nodes (size of this array = leaf count)
|
||||
public static final byte POST2_LLD = 8; // left-most leaf descendants
|
||||
public static final byte POST2_MIN_KR = 9; // minimum key root nodes index in KR array
|
||||
public static final byte RKR = 10; // reversed key root nodes
|
||||
public static final byte RPOST2_RLD = 11; // reversed postorer 2 right-most leaf descendants
|
||||
public static final byte RPOST2_MIN_RKR = 12;
|
||||
public static final byte RPOST2_POST = 13; // reversed postorder -> postorder
|
||||
public static final byte POST2_STRATEGY = 14; // strategy for Demaine (is there sth on the left/right of the heavy node)
|
||||
public static final byte PRE2_POST = 15;
|
||||
|
||||
public int[][] info; // an array with all the indeces
|
||||
|
||||
private LabelDictionary ld; // dictionary with labels - common for two input trees
|
||||
|
||||
public boolean[][] nodeType; // store the type of a node: for every node stores three boolean values (L, R, H)
|
||||
|
||||
// paths and rel subtrees are inside 2D arrays to be able to get them by paths/relsubtrees[L/R/H][node]
|
||||
private int[][] paths;
|
||||
private int[][][] relSubtrees;
|
||||
|
||||
// temporal variables
|
||||
private int sizeTmp = 0; // temporal value of size of a subtree
|
||||
private int descSizesTmp = 0; // temporal value of sum of descendat sizes
|
||||
private int krSizesSumTmp = 0; // temporal value of sum of key roots sizes
|
||||
private int revkrSizesSumTmp = 0; // temporal value of sum of reversed hey roots sizes
|
||||
private int preorderTmp = 0; // temporal value of preorder
|
||||
|
||||
// remembers what is the current node's postorder (current in the TED recursion)
|
||||
private int currentNode = -1;
|
||||
|
||||
// remembers if the trees order was switched during the recursion (in comparison with the order of input trees)
|
||||
private boolean switched = false;
|
||||
|
||||
// as the names say
|
||||
private int leafCount = 0;
|
||||
private int treeSize = 0;
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an InfoTree object, gathers all information about aInputTree and stores in indexes.
|
||||
* aInputTree is not needed any more.
|
||||
* Remember to pass the same LabelDictionary object to both trees which are compared.
|
||||
*
|
||||
* @param aInputTree an LblTree object
|
||||
* @param aLd a LabelDictionary object
|
||||
*/
|
||||
public InfoTree(ITree aInputTree, LabelDictionary aLd) {
|
||||
this.inputTree = aInputTree;
|
||||
treeSize = inputTree.getSize();
|
||||
this.info = new int[16][treeSize];
|
||||
Arrays.fill(info[POST2_PARENT], -1);
|
||||
Arrays.fill(info[POST2_MIN_KR], -1);
|
||||
Arrays.fill(info[RPOST2_MIN_RKR], -1);
|
||||
Arrays.fill(info[POST2_STRATEGY], -1);
|
||||
this.paths = new int[3][treeSize]; Arrays.fill(paths[LEFT], -1); Arrays.fill(paths[RIGHT], -1); Arrays.fill(paths[HEAVY], -1);
|
||||
this.relSubtrees = new int[3][treeSize][];
|
||||
this.nodeType = new boolean[3][treeSize];
|
||||
this.ld = aLd;
|
||||
this.currentNode = treeSize - 1;
|
||||
gatherInfo(inputTree, -1);
|
||||
postTraversalProcessing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the tree.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getSize() {
|
||||
return treeSize;
|
||||
}
|
||||
|
||||
public boolean ifNodeOfType(int postorder, int type) {
|
||||
return nodeType[type][postorder];
|
||||
}
|
||||
|
||||
public boolean[] getNodeTypeArray(int type) {
|
||||
return nodeType[type];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For given infoCode and postorder of a node returns requested information of that node.
|
||||
*
|
||||
* @param infoCode
|
||||
* @param nodesPostorder postorder of a node
|
||||
* @return a value of requested information
|
||||
*/
|
||||
public int getInfo(int infoCode, int nodesPostorder) {
|
||||
// return info under infoCode and nodesPostorder
|
||||
return info[infoCode][nodesPostorder];
|
||||
}
|
||||
|
||||
/**
|
||||
* For given infoCode returns an info array (index array)
|
||||
*
|
||||
* @param infoCode
|
||||
* @return array with requested index
|
||||
*/
|
||||
public int[] getInfoArray(int infoCode) {
|
||||
return info[infoCode];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns relevant subtrees for given node. Assuming that child v of given node belongs to given path,
|
||||
* all children of given node are returned but node v.
|
||||
*
|
||||
* @param pathType
|
||||
* @param nodePostorder postorder of a node
|
||||
* @return an array with relevant subtrees of a given node
|
||||
*/
|
||||
public int[] getNodeRelSubtrees(int pathType, int nodePostorder) {
|
||||
return relSubtrees[pathType][nodePostorder];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array representation of a given path's type.
|
||||
*
|
||||
* @param pathType
|
||||
* @return an array with a requested path
|
||||
*/
|
||||
public int[] getPath(int pathType) {
|
||||
return paths[pathType];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the postorder of current root node.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getCurrentNode() {
|
||||
return currentNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets postorder of the current node in the recursion.
|
||||
*
|
||||
* @param postorder
|
||||
*/
|
||||
public void setCurrentNode(int postorder) {
|
||||
currentNode = postorder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers information of a given tree in corresponding arrays.
|
||||
*
|
||||
* At this point the given tree is traversed once, but there is a loop over current nodes children
|
||||
* to assign them their parents.
|
||||
*
|
||||
* @param aT
|
||||
* @param postorder
|
||||
* @return
|
||||
*/
|
||||
private int gatherInfo(ITree aT, int postorder) {
|
||||
int currentSize = 0;
|
||||
int childrenCount = 0;
|
||||
int descSizes = 0;
|
||||
int krSizesSum = 0;
|
||||
int revkrSizesSum = 0;
|
||||
int preorder = preorderTmp;
|
||||
|
||||
int heavyChild = -1;
|
||||
int leftChild = -1;
|
||||
int rightChild = -1;
|
||||
int weight = -1;
|
||||
int maxWeight = -1;
|
||||
int currentPostorder = -1;
|
||||
int oldHeavyChild = -1;
|
||||
|
||||
ArrayList heavyRelSubtreesTmp = new ArrayList();
|
||||
ArrayList leftRelSubtreesTmp = new ArrayList();
|
||||
ArrayList rightRelSubtreesTmp = new ArrayList();
|
||||
|
||||
ArrayList<Integer> childrenPostorders = new ArrayList<Integer>();
|
||||
|
||||
preorderTmp++;
|
||||
|
||||
// enumerate over children of current node
|
||||
for (Enumeration<?> e = Collections.enumeration(aT.getChildren()); e.hasMoreElements();) {
|
||||
childrenCount++;
|
||||
|
||||
postorder = gatherInfo((ITree) e.nextElement(), postorder);
|
||||
|
||||
childrenPostorders.add(postorder);
|
||||
|
||||
currentPostorder = postorder;
|
||||
|
||||
// heavy path
|
||||
weight = sizeTmp + 1;
|
||||
if (weight >= maxWeight) {
|
||||
maxWeight = weight;
|
||||
oldHeavyChild = heavyChild;
|
||||
heavyChild = currentPostorder;
|
||||
} else heavyRelSubtreesTmp.add(currentPostorder);
|
||||
|
||||
if (oldHeavyChild != -1) {
|
||||
heavyRelSubtreesTmp.add(oldHeavyChild);
|
||||
oldHeavyChild = -1;
|
||||
}
|
||||
|
||||
// left path
|
||||
if (childrenCount == 1) leftChild = currentPostorder;
|
||||
else leftRelSubtreesTmp.add(currentPostorder);
|
||||
|
||||
// right path
|
||||
rightChild = currentPostorder;
|
||||
if (e.hasMoreElements()) rightRelSubtreesTmp.add(currentPostorder);
|
||||
|
||||
// subtree size
|
||||
currentSize += 1 + sizeTmp;
|
||||
|
||||
descSizes += descSizesTmp;
|
||||
|
||||
if (childrenCount > 1) krSizesSum += krSizesSumTmp + sizeTmp + 1;
|
||||
else {
|
||||
krSizesSum += krSizesSumTmp;
|
||||
nodeType[LEFT][currentPostorder] = true;
|
||||
}
|
||||
|
||||
if (e.hasMoreElements()) revkrSizesSum += revkrSizesSumTmp + sizeTmp + 1;
|
||||
else {
|
||||
revkrSizesSum += revkrSizesSumTmp;
|
||||
nodeType[RIGHT][currentPostorder] = true;
|
||||
}
|
||||
}
|
||||
|
||||
postorder++;
|
||||
|
||||
int currentDescSizes = descSizes + currentSize + 1;
|
||||
info[POST2_DESC_SUM][postorder] = (currentSize + 1) * (currentSize + 1 + 3) / 2 - currentDescSizes;
|
||||
info[POST2_KR_SUM][postorder] = krSizesSum + currentSize + 1;
|
||||
info[POST2_REV_KR_SUM][postorder] = revkrSizesSum + currentSize + 1;
|
||||
|
||||
// POST2_LABEL
|
||||
//labels[rootNumber] = ld.store(aT.getLabel());
|
||||
info[POST2_LABEL][postorder] = ld.store(aT.getLabel());
|
||||
|
||||
// POST2_PARENT
|
||||
for (Integer i : childrenPostorders) info[POST2_PARENT][i] = postorder;
|
||||
|
||||
// POST2_SIZE
|
||||
info[POST2_SIZE][postorder] = currentSize + 1;
|
||||
if (currentSize == 0) leafCount++;
|
||||
|
||||
// POST2_PRE
|
||||
info[POST2_PRE][postorder] = preorder;
|
||||
|
||||
// PRE2_POST
|
||||
info[PRE2_POST][preorder] = postorder;
|
||||
|
||||
// RPOST2_POST
|
||||
info[RPOST2_POST][treeSize - 1 - preorder] = postorder;
|
||||
|
||||
// heavy path
|
||||
if (heavyChild != -1) {
|
||||
paths[HEAVY][postorder] = heavyChild;
|
||||
nodeType[HEAVY][heavyChild] = true;
|
||||
|
||||
if (leftChild < heavyChild && heavyChild < rightChild) {
|
||||
info[POST2_STRATEGY][postorder] = BOTH;
|
||||
} else if (heavyChild == leftChild) {
|
||||
info[POST2_STRATEGY][postorder] = RIGHT;
|
||||
} else if (heavyChild == rightChild) {
|
||||
info[POST2_STRATEGY][postorder] = LEFT;
|
||||
}
|
||||
} else {
|
||||
info[POST2_STRATEGY][postorder] = RIGHT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// left path
|
||||
if (leftChild != -1) {
|
||||
paths[LEFT][postorder] = leftChild;
|
||||
}
|
||||
// right path
|
||||
if (rightChild != -1) {
|
||||
paths[RIGHT][postorder] = rightChild;
|
||||
}
|
||||
|
||||
// heavy/left/right relevant subtrees
|
||||
relSubtrees[HEAVY][postorder] = toIntArray(heavyRelSubtreesTmp);
|
||||
relSubtrees[RIGHT][postorder] = toIntArray(rightRelSubtreesTmp);
|
||||
relSubtrees[LEFT][postorder] = toIntArray(leftRelSubtreesTmp);
|
||||
|
||||
descSizesTmp = currentDescSizes;
|
||||
sizeTmp = currentSize;
|
||||
krSizesSumTmp = krSizesSum;
|
||||
revkrSizesSumTmp = revkrSizesSum;
|
||||
|
||||
return postorder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers information, that couldn't be collected while tree traversal.
|
||||
*/
|
||||
private void postTraversalProcessing() {
|
||||
int nc1 = treeSize;
|
||||
info[KR] = new int[leafCount];
|
||||
info[RKR] = new int[leafCount];
|
||||
|
||||
int nc = nc1;
|
||||
int lc = leafCount;
|
||||
int i = 0;
|
||||
|
||||
// compute left-most leaf descendants
|
||||
// go along the left-most path, remember each node and assign to it the path's leaf
|
||||
// compute right-most leaf descendants (in reversed postorder)
|
||||
for (i = 0; i < treeSize; i++) {
|
||||
if (paths[LEFT][i] == -1) {
|
||||
info[POST2_LLD][i] = i;
|
||||
} else {
|
||||
info[POST2_LLD][i] = info[POST2_LLD][paths[LEFT][i]];
|
||||
}
|
||||
if (paths[RIGHT][i] == -1) {
|
||||
info[RPOST2_RLD][treeSize - 1 - info[POST2_PRE][i]] = (treeSize - 1 - info[POST2_PRE][i]);
|
||||
} else {
|
||||
info[RPOST2_RLD][treeSize - 1 - info[POST2_PRE][i]] = info[RPOST2_RLD][treeSize - 1 - info[POST2_PRE][paths[RIGHT][i]]];
|
||||
}
|
||||
}
|
||||
|
||||
// compute key root nodes
|
||||
// compute reversed key root nodes (in revrsed postorder)
|
||||
boolean[] visited = new boolean[nc];
|
||||
boolean[] visitedR = new boolean[nc];
|
||||
Arrays.fill(visited, false);
|
||||
int k = lc - 1;
|
||||
int kR = lc - 1;
|
||||
for (i = nc - 1; i >= 0; i--) {
|
||||
if (!visited[info[POST2_LLD][i]]) {
|
||||
info[KR][k] = i;
|
||||
visited[info[POST2_LLD][i]] = true;
|
||||
k--;
|
||||
}
|
||||
if (!visitedR[info[RPOST2_RLD][i]]) {
|
||||
info[RKR][kR] = i;
|
||||
visitedR[info[RPOST2_RLD][i]] = true;
|
||||
kR--;
|
||||
}
|
||||
}
|
||||
|
||||
// compute minimal key roots for every subtree
|
||||
// compute minimal reversed key roots for every subtree (in reversed postorder)
|
||||
int parent = -1;
|
||||
int parentR = -1;
|
||||
for (i = 0; i < leafCount; i++) {
|
||||
parent = info[KR][i];
|
||||
while (parent > -1 && info[POST2_MIN_KR][parent] == -1) {
|
||||
info[POST2_MIN_KR][parent] = i;
|
||||
parent = info[POST2_PARENT][parent];
|
||||
}
|
||||
parentR = info[RKR][i];
|
||||
while (parentR > -1 && info[RPOST2_MIN_RKR][parentR] == -1) {
|
||||
info[RPOST2_MIN_RKR][parentR] = i;
|
||||
parentR = info[POST2_PARENT][info[RPOST2_POST][parentR]]; // get parent's postorder
|
||||
if (parentR > -1) {
|
||||
parentR = treeSize - 1 - info[POST2_PRE][parentR]; // if parent exists get its rev. postorder
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a list of Integer objects to an array of primitive int values.
|
||||
* @param integers
|
||||
* @return
|
||||
*/
|
||||
public static int[] toIntArray(List<Integer> integers) {
|
||||
int[] ints = new int[integers.size()];
|
||||
int i = 0;
|
||||
for (Integer n : integers) {
|
||||
ints[i++] = n;
|
||||
}
|
||||
return ints;
|
||||
}
|
||||
|
||||
public void setSwitched(boolean value) {
|
||||
switched = value;
|
||||
}
|
||||
|
||||
public boolean isSwitched() {
|
||||
return switched;
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
// Copyright (C) 2012 Mateusz Pawlik and Nikolaus Augsten
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package com.github.gumtreediff.matchers.optimal.rted;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This provides a way of using small int values to represent String labels,
|
||||
* as opposed to storing the labels directly.
|
||||
*
|
||||
* @author Denilson Barbosa, Nikolaus Augsten from approxlib, available at http://www.inf.unibz.it/~augsten/src/
|
||||
*/
|
||||
public class LabelDictionary {
|
||||
public static final int KEY_DUMMY_LABEL = -1;
|
||||
private int count;
|
||||
private Map<String, Integer> strInt;
|
||||
private Map<Integer, String> intStr;
|
||||
private boolean newLabelsAllowed = true;
|
||||
|
||||
/**
|
||||
* Creates a new blank dictionary.
|
||||
*/
|
||||
public LabelDictionary() {
|
||||
count = 0;
|
||||
strInt = new Hashtable<String, Integer>();
|
||||
intStr = new Hashtable<Integer, String>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a new label to the dictionary if it has not been added yet.
|
||||
* Returns the ID of the new label in the dictionary.
|
||||
*
|
||||
* @param label add this label to the dictionary if it does not exist yet
|
||||
* @return ID of label in the dictionary
|
||||
*/
|
||||
public int store(String label) {
|
||||
if (strInt.containsKey(label)) {
|
||||
return (strInt.get(label).intValue());
|
||||
} else if (!newLabelsAllowed) {
|
||||
return KEY_DUMMY_LABEL;
|
||||
} else { // store label
|
||||
Integer intKey = new Integer(count++);
|
||||
strInt.put(label, intKey);
|
||||
intStr.put(intKey, label);
|
||||
|
||||
return intKey.intValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label with a given ID in the dictionary.
|
||||
*
|
||||
* @param labelID
|
||||
* @return the label with the specified labelID, or null if this dictionary contains no label for labelID
|
||||
*/
|
||||
public String read(int labelID) {
|
||||
return intStr.get(new Integer(labelID));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff new labels can be stored into this label dictinoary
|
||||
*/
|
||||
public boolean isNewLabelsAllowed() {
|
||||
return newLabelsAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newLabelsAllowed the newLabelsAllowed to set
|
||||
*/
|
||||
public void setNewLabelsAllowed(boolean newLabelsAllowed) {
|
||||
this.newLabelsAllowed = newLabelsAllowed;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1182
File diff suppressed because it is too large
Load Diff
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.optimal.rted;
|
||||
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RtedMatcher extends Matcher {
|
||||
|
||||
public RtedMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void match() {
|
||||
RtedAlgorithm a = new RtedAlgorithm(1D, 1D, 1D);
|
||||
a.init(src, dst);
|
||||
a.computeOptimalStrategy();
|
||||
a.nonNormalizedTreeDist();
|
||||
List<int[]> arrayMappings = a.computeEditMapping();
|
||||
List<ITree> srcs = TreeUtils.postOrder(src);
|
||||
List<ITree> dsts = TreeUtils.postOrder(dst);
|
||||
for (int[] m: arrayMappings) {
|
||||
if (m[0] != 0 && m[1] != 0) {
|
||||
ITree src = srcs.get(m[0] - 1);
|
||||
ITree dst = dsts.get(m[1] - 1);
|
||||
if (isMappingAllowed(src, dst))
|
||||
addMapping(src, dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.matchers.optimal.zs;
|
||||
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import org.simmetrics.StringMetrics;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
|
||||
public class ZsMatcher extends Matcher {
|
||||
|
||||
private ZsTree src;
|
||||
private ZsTree dst;
|
||||
|
||||
private double[][] treeDist;
|
||||
private double[][] forestDist;
|
||||
|
||||
private static ITree getFirstLeaf(ITree t) {
|
||||
ITree current = t;
|
||||
while (!current.isLeaf())
|
||||
current = current.getChild(0);
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
public ZsMatcher(ITree src, ITree dst, MappingStore store) {
|
||||
super(src, dst, store);
|
||||
this.src = new ZsTree(src);
|
||||
this.dst = new ZsTree(dst);
|
||||
}
|
||||
|
||||
private double[][] computeTreeDist() {
|
||||
|
||||
treeDist = new double[src.nodeCount + 1][dst.nodeCount + 1];
|
||||
forestDist = new double[src.nodeCount + 1][dst.nodeCount + 1];
|
||||
|
||||
for (int i = 1; i < src.kr.length; i++) {
|
||||
for (int j = 1; j < dst.kr.length; j++) {
|
||||
forestDist(src.kr[i], dst.kr[j]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return treeDist;
|
||||
}
|
||||
|
||||
private void forestDist(int i, int j) {
|
||||
forestDist[src.lld(i) - 1][dst.lld(j) - 1] = 0;
|
||||
for (int di = src.lld(i); di <= i; di++) {
|
||||
double costDel = getDeletionCost(src.tree(di));
|
||||
forestDist[di][dst.lld(j) - 1] = forestDist[di - 1][dst.lld(j) - 1] + costDel;
|
||||
for (int dj = dst.lld(j); dj <= j; dj++) {
|
||||
double costIns = getInsertionCost(dst.tree(dj));
|
||||
forestDist[src.lld(i) - 1][dj] = forestDist[src.lld(i) - 1][dj - 1] + costIns;
|
||||
|
||||
if ((src.lld(di) == src.lld(i) && (dst.lld(dj) == dst.lld(j)))) {
|
||||
double costUpd = getUpdateCost(src.tree(di), dst.tree(dj));
|
||||
forestDist[di][dj] = Math.min(Math.min(forestDist[di - 1][dj] + costDel,
|
||||
forestDist[di][dj - 1] + costIns),
|
||||
forestDist[di - 1][dj - 1] + costUpd);
|
||||
treeDist[di][dj] = forestDist[di][dj];
|
||||
} else {
|
||||
forestDist[di][dj] = Math.min(Math.min(forestDist[di - 1][dj] + costDel,
|
||||
forestDist[di][dj - 1] + costIns),
|
||||
forestDist[src.lld(di) - 1][dst.lld(dj) - 1]
|
||||
+ treeDist[di][dj]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void match() {
|
||||
computeTreeDist();
|
||||
|
||||
boolean rootNodePair = true;
|
||||
|
||||
LinkedList<int[]> treePairs = new LinkedList<int[]>();
|
||||
|
||||
// push the pair of trees (ted1,ted2) to stack
|
||||
treePairs.push(new int[] { src.nodeCount, dst.nodeCount });
|
||||
|
||||
while (!treePairs.isEmpty()) {
|
||||
int[] treePair = treePairs.pop();
|
||||
|
||||
int lastRow = treePair[0];
|
||||
int lastCol = treePair[1];
|
||||
|
||||
// compute forest distance matrix
|
||||
if (!rootNodePair)
|
||||
forestDist(lastRow, lastCol);
|
||||
|
||||
rootNodePair = false;
|
||||
|
||||
// compute mapping for current forest distance matrix
|
||||
int firstRow = src.lld(lastRow) - 1;
|
||||
int firstCol = dst.lld(lastCol) - 1;
|
||||
|
||||
int row = lastRow;
|
||||
int col = lastCol;
|
||||
|
||||
while ((row > firstRow) || (col > firstCol)) {
|
||||
if ((row > firstRow)
|
||||
&& (forestDist[row - 1][col] + 1D == forestDist[row][col])) {
|
||||
// node with postorderID row is deleted from ted1
|
||||
row--;
|
||||
} else if ((col > firstCol)
|
||||
&& (forestDist[row][col - 1] + 1D == forestDist[row][col])) {
|
||||
// node with postorderID col is inserted into ted2
|
||||
col--;
|
||||
} else {
|
||||
// node with postorderID row in ted1 is renamed to node col
|
||||
// in ted2
|
||||
if ((src.lld(row) - 1 == src.lld(lastRow) - 1) && (dst.lld(col) - 1 == dst.lld(lastCol) - 1)) {
|
||||
// if both subforests are trees, map nodes
|
||||
ITree tSrc = src.tree(row);
|
||||
ITree tDst = dst.tree(col);
|
||||
if (tSrc.getType() == tDst.getType())
|
||||
addMapping(tSrc, tDst);
|
||||
else
|
||||
throw new RuntimeException("Should not map incompatible nodes.");
|
||||
row--;
|
||||
col--;
|
||||
} else {
|
||||
// pop subtree pair
|
||||
treePairs.push(new int[] { row, col });
|
||||
// continue with forest to the left of the popped
|
||||
// subtree pair
|
||||
|
||||
row = src.lld(row) - 1;
|
||||
col = dst.lld(col) - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double getDeletionCost(ITree n) {
|
||||
return 1D;
|
||||
}
|
||||
|
||||
private double getInsertionCost(ITree n) {
|
||||
return 1D;
|
||||
}
|
||||
|
||||
private double getUpdateCost(ITree n1, ITree n2) {
|
||||
if (n1.getType() == n2.getType())
|
||||
if ("".equals(n1.getLabel()) || "".equals(n2.getLabel()))
|
||||
return 1D;
|
||||
else
|
||||
return 1D - StringMetrics.qGramsDistance().compare(n1.getLabel(), n2.getLabel());
|
||||
else
|
||||
return Double.MAX_VALUE;
|
||||
}
|
||||
|
||||
private final class ZsTree {
|
||||
|
||||
private int start; // internal array position of leafmost leaf descendant of the root node
|
||||
|
||||
private int nodeCount; // number of nodes
|
||||
|
||||
private int leafCount;
|
||||
|
||||
private int[] llds; // llds[i] stores the postorder-ID of the
|
||||
// left-most leaf descendant of the i-th node in postorder
|
||||
private ITree[] labels; // labels[i] is the tree of the i-th node in postorder
|
||||
|
||||
private int[] kr;
|
||||
|
||||
private ZsTree(ITree t) {
|
||||
this.start = 0;
|
||||
this.nodeCount = t.getSize();
|
||||
this.leafCount = 0;
|
||||
this.llds = new int[start + nodeCount];
|
||||
this.labels = new ITree[start + nodeCount];
|
||||
|
||||
int idx = 1;
|
||||
Map<ITree,Integer> tmpData = new HashMap<>();
|
||||
for (ITree n: t.postOrder()) {
|
||||
tmpData.put(n, idx);
|
||||
this.setITree(idx, n);
|
||||
this.setLld(idx, tmpData.get(ZsMatcher.getFirstLeaf(n)));
|
||||
if (n.isLeaf())
|
||||
leafCount++;
|
||||
idx++;
|
||||
}
|
||||
|
||||
setKeyRoots();
|
||||
}
|
||||
|
||||
public void setITree(int i, ITree tree) {
|
||||
labels[i + start - 1] = tree;
|
||||
if (nodeCount < i)
|
||||
nodeCount = i;
|
||||
}
|
||||
|
||||
public void setLld(int i, int lld) {
|
||||
llds[i + start - 1] = lld + start - 1;
|
||||
if (nodeCount < i)
|
||||
nodeCount = i;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public boolean isLeaf(int i) {
|
||||
return this.lld(i) == i;
|
||||
}
|
||||
|
||||
public int lld(int i) {
|
||||
return llds[i + start - 1] - start + 1;
|
||||
}
|
||||
|
||||
public ITree tree(int i) {
|
||||
return labels[i + start - 1];
|
||||
}
|
||||
|
||||
public void setKeyRoots() {
|
||||
kr = new int[leafCount + 1];
|
||||
boolean[] visited = new boolean[nodeCount + 1];
|
||||
Arrays.fill(visited, false);
|
||||
int k = kr.length - 1;
|
||||
for (int i = nodeCount; i >= 1; i--) {
|
||||
if (!visited[lld(i)]) {
|
||||
kr[k] = i;
|
||||
visited[lld(i)] = true;
|
||||
k--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.tree;
|
||||
|
||||
import com.github.gumtreediff.tree.hash.HashUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class AbstractTree implements ITree,Serializable {
|
||||
|
||||
protected int id;
|
||||
|
||||
protected ITree parent;
|
||||
|
||||
protected List<ITree> children;
|
||||
|
||||
protected int height;
|
||||
|
||||
protected int size;
|
||||
|
||||
protected int depth;
|
||||
|
||||
protected int hash;
|
||||
|
||||
@Override
|
||||
public String getChildrenLabels() {
|
||||
StringBuffer b = new StringBuffer();
|
||||
for (ITree child: getChildren())
|
||||
if (!"".equals(child.getLabel()))
|
||||
b.append(child.getLabel() + " ");
|
||||
return b.toString().trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildPosition(ITree child) {
|
||||
return getChildren().indexOf(child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITree getChild(int position) {
|
||||
return getChildren().get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDepth() {
|
||||
return depth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ITree> getDescendants() {
|
||||
List<ITree> trees = TreeUtils.preOrder(this);
|
||||
trees.remove(0);
|
||||
return trees;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLabel() {
|
||||
return !NO_LABEL.equals(getLabel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITree getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParent(ITree parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ITree> getParents() {
|
||||
List<ITree> parents = new ArrayList<>();
|
||||
if (getParent() == null)
|
||||
return parents;
|
||||
else {
|
||||
parents.add(getParent());
|
||||
parents.addAll(getParent().getParents());
|
||||
}
|
||||
return parents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ITree> getTrees() {
|
||||
return TreeUtils.preOrder(this);
|
||||
}
|
||||
|
||||
private String indent(ITree t) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (int i = 0; i < t.getDepth(); i++)
|
||||
b.append("\t");
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIsomorphicTo(ITree tree) {
|
||||
if (this.getHash() != tree.getHash())
|
||||
return false;
|
||||
else
|
||||
return this.toStaticHashString().equals(tree.toStaticHashString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSameType(ITree t) {
|
||||
return getType() == t.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeaf() {
|
||||
return getChildren().size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRoot() {
|
||||
return getParent() == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSameTypeAndLabel(ITree t) {
|
||||
if (!hasSameType(t))
|
||||
return false;
|
||||
else if (!getLabel().equals(t.getLabel()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ITree> preOrder() {
|
||||
return new Iterable<ITree>() {
|
||||
@Override
|
||||
public Iterator<ITree> iterator() {
|
||||
return TreeUtils.preOrderIterator(AbstractTree.this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ITree> postOrder() {
|
||||
return new Iterable<ITree>() {
|
||||
@Override
|
||||
public Iterator<ITree> iterator() {
|
||||
return TreeUtils.postOrderIterator(AbstractTree.this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ITree> breadthFirst() {
|
||||
return new Iterable<ITree>() {
|
||||
@Override
|
||||
public Iterator<ITree> iterator() {
|
||||
return TreeUtils.breadthFirstIterator(AbstractTree.this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int positionInParent() {
|
||||
ITree p = getParent();
|
||||
if (p == null)
|
||||
return -1;
|
||||
else
|
||||
return p.getChildren().indexOf(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
TreeUtils.computeSize(this);
|
||||
TreeUtils.computeDepth(this);
|
||||
TreeUtils.computeHeight(this);
|
||||
HashUtils.DEFAULT_HASH_GENERATOR.hash(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDepth(int depth) {
|
||||
this.depth = depth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHash(int digest) {
|
||||
this.hash = digest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeight(int height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStaticHashString() {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(OPEN_SYMBOL);
|
||||
b.append(this.toShortString());
|
||||
for (ITree c: this.getChildren())
|
||||
b.append(c.toStaticHashString());
|
||||
b.append(CLOSE_SYMBOL);
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
System.err.println("This method should currently not be used (please use toShortString())");
|
||||
return toShortString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toShortString() {
|
||||
return String.format("%d%s%s", getType(), SEPARATE_SYMBOL, getLabel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toTreeString() {
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (ITree t : TreeUtils.preOrder(this))
|
||||
b.append(indent(t) + t.toShortString() + "\n");
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toPrettyString(TreeContext ctx) {
|
||||
if (hasLabel())
|
||||
return ctx.getTypeLabel(this) + ": " + getLabel();
|
||||
else
|
||||
return ctx.getTypeLabel(this);
|
||||
}
|
||||
|
||||
public static class FakeTree extends AbstractTree {
|
||||
public FakeTree(ITree... trees) {
|
||||
children = new ArrayList<>(trees.length);
|
||||
children.addAll(Arrays.asList(trees));
|
||||
}
|
||||
|
||||
private RuntimeException unsupportedOperation() {
|
||||
return new UnsupportedOperationException("This method should not be called on a fake tree");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChild(ITree t) {
|
||||
throw unsupportedOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertChild(ITree t, int position) {
|
||||
throw unsupportedOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITree deepCopy() {
|
||||
throw unsupportedOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ITree> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return NO_LABEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return getEndPos() - getPos();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPos() {
|
||||
return Collections.min(children, (t1, t2) -> t2.getPos() - t1.getPos()).getPos();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndPos() {
|
||||
return Collections.max(children, (t1, t2) -> t2.getPos() - t1.getPos()).getEndPos();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChildren(List<ITree> children) {
|
||||
throw unsupportedOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
throw unsupportedOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLength(int length) {
|
||||
throw unsupportedOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParentAndUpdateChildren(ITree parent) {
|
||||
throw unsupportedOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPos(int pos) {
|
||||
throw unsupportedOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(int type) {
|
||||
throw unsupportedOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toPrettyString(TreeContext ctx) {
|
||||
return "FakeTree";
|
||||
}
|
||||
|
||||
/**
|
||||
* fake nodes have no metadata
|
||||
*/
|
||||
@Override
|
||||
public Object getMetadata(String key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* fake node store no metadata
|
||||
*/
|
||||
@Override
|
||||
public Object setMetadata(String key, Object value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since they have no metadata they do not iterate on nothing
|
||||
*/
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, Object>> getMetadata() {
|
||||
return new EmptyEntryIterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class EmptyEntryIterator implements Iterator<Map.Entry<String, Object>> {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<String, Object> next() {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.tree;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class AssociationMap {
|
||||
// FIXME or not, should we inline this class ? or use Entry to only have one list ? ... or both
|
||||
ArrayList<Object> values = new ArrayList<>();
|
||||
ArrayList<String> keys = new ArrayList<>();
|
||||
|
||||
public Object get(String key) {
|
||||
int idx = keys.indexOf(key);
|
||||
if (idx == -1)
|
||||
return null;
|
||||
return values.get(idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* set metadata `key` with `value` and returns the previous value
|
||||
* This method won't remove if value == null
|
||||
*/
|
||||
public Object set(String key, Object value) {
|
||||
int idx = keys.indexOf(key);
|
||||
if (idx == -1) {
|
||||
keys.add(key);
|
||||
values.add(value);
|
||||
return null;
|
||||
}
|
||||
return values.set(idx, value);
|
||||
}
|
||||
|
||||
public Object remove(String key) {
|
||||
int idx = keys.indexOf(key);
|
||||
if (idx == -1)
|
||||
return null;
|
||||
if (idx == keys.size() - 1) {
|
||||
keys.remove(idx);
|
||||
return values.remove(idx);
|
||||
}
|
||||
keys.set(idx, keys.remove(keys.size() - 1));
|
||||
return values.set(idx, values.remove(values.size() - 1));
|
||||
}
|
||||
|
||||
public Iterator<Entry<String, Object>> iterator() {
|
||||
return new Iterator<Entry<String, Object>>() {
|
||||
int currentPos = 0;
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return currentPos < keys.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<String, Object> next() {
|
||||
Entry<String, Object> e = new AbstractMap.SimpleEntry<>(keys.get(currentPos), values.get(currentPos));
|
||||
currentPos++;
|
||||
return e;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.tree;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Interface to represent abstract syntax trees.
|
||||
*/
|
||||
public interface ITree {
|
||||
|
||||
String OPEN_SYMBOL = "[(";
|
||||
String CLOSE_SYMBOL = ")]";
|
||||
String SEPARATE_SYMBOL = "@@";
|
||||
|
||||
int NO_ID = Integer.MIN_VALUE;
|
||||
|
||||
String NO_LABEL = "";
|
||||
|
||||
int NO_VALUE = -1;
|
||||
|
||||
/**
|
||||
* @see com.github.gumtreediff.tree.hash.HashGenerator
|
||||
* @return a hash (probably unique) representing the tree
|
||||
*/
|
||||
int getHash();
|
||||
|
||||
void setHash(int hash);
|
||||
|
||||
/**
|
||||
* @return all the nodes contained in the tree, using a pre-order.
|
||||
*/
|
||||
List<ITree> getTrees();
|
||||
|
||||
Iterable<ITree> preOrder();
|
||||
|
||||
Iterable<ITree> postOrder();
|
||||
|
||||
Iterable<ITree> breadthFirst();
|
||||
|
||||
/**
|
||||
* Add the given tree as a child, and update its parent.
|
||||
*/
|
||||
void addChild(ITree t);
|
||||
|
||||
/**
|
||||
* Insert the given tree as the position-th child, and update its parent.
|
||||
*/
|
||||
void insertChild(ITree t, int position);
|
||||
|
||||
void setChildren(List<ITree> children);
|
||||
|
||||
/**
|
||||
* @return the position of the child, or -1 if the given child is not in the children list.
|
||||
*/
|
||||
int getChildPosition(ITree child);
|
||||
|
||||
/**
|
||||
* @param position the child position, starting at 0
|
||||
*/
|
||||
ITree getChild(int position);
|
||||
|
||||
List<ITree> getChildren();
|
||||
|
||||
/**
|
||||
* @return a boolean indicating if the tree has at least one child or not
|
||||
*/
|
||||
boolean isLeaf();
|
||||
|
||||
/**
|
||||
* @return all the descendants (children, children of children, etc.) of the tree,
|
||||
* using a pre-order.
|
||||
*/
|
||||
List<ITree> getDescendants();
|
||||
|
||||
/**
|
||||
* Set the parent of this node. The parent won't have this node in its
|
||||
* children list
|
||||
*/
|
||||
void setParent(ITree parent);
|
||||
|
||||
/**
|
||||
* Set the parent of this node. The parent will have this node in its
|
||||
* children list, at the last position
|
||||
*/
|
||||
void setParentAndUpdateChildren(ITree parent);
|
||||
|
||||
/**
|
||||
* @return a boolean indicating if the tree has a parent or not
|
||||
*/
|
||||
boolean isRoot();
|
||||
|
||||
ITree getParent();
|
||||
|
||||
/**
|
||||
* @return the list of all parents of the node (parent, parent of parent, etc.)
|
||||
*/
|
||||
List<ITree> getParents();
|
||||
|
||||
/**
|
||||
* @return the position of the node in its parent children list
|
||||
*/
|
||||
int positionInParent();
|
||||
|
||||
/**
|
||||
* Make a deep copy of the tree.
|
||||
* Deep copy of node however shares Metadata
|
||||
* @return a deep copy of the tree.
|
||||
*/
|
||||
ITree deepCopy();
|
||||
|
||||
/**
|
||||
* @see TreeUtils#computeDepth(ITree)
|
||||
* @return the depth of the tree, defined as the distance to the root
|
||||
*/
|
||||
int getDepth();
|
||||
|
||||
void setDepth(int depth);
|
||||
|
||||
/**
|
||||
* @see TreeUtils#computeHeight(ITree)
|
||||
* @return the height of the tree, defined as the maximal depth of its descendants.
|
||||
*/
|
||||
int getHeight();
|
||||
|
||||
void setHeight(int height);
|
||||
|
||||
/**
|
||||
* @see TreeUtils#numbering(Iterable)
|
||||
* @see TreeUtils#preOrderNumbering(ITree)
|
||||
* @see TreeUtils#postOrderNumbering(ITree)
|
||||
* @see TreeUtils#breadthFirstNumbering(ITree)
|
||||
* @return the number of the node
|
||||
*/
|
||||
int getId();
|
||||
|
||||
void setId(int id);
|
||||
|
||||
boolean hasLabel();
|
||||
|
||||
String getLabel();
|
||||
|
||||
void setLabel(String label);
|
||||
|
||||
int getPos();
|
||||
|
||||
void setPos(int pos);
|
||||
|
||||
int getLength();
|
||||
|
||||
void setLength(int length);
|
||||
|
||||
/**
|
||||
* @return the absolute character index where the tree ends
|
||||
*/
|
||||
default int getEndPos() {
|
||||
return getPos() + getLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TreeUtils#computeSize(ITree)
|
||||
* @return the number of all nodes contained in the tree
|
||||
*/
|
||||
int getSize();
|
||||
|
||||
void setSize(int size);
|
||||
|
||||
int getType();
|
||||
|
||||
void setType(int type);
|
||||
|
||||
/**
|
||||
* @return a boolean indicating if the trees have the same type.
|
||||
*/
|
||||
boolean hasSameType(ITree t);
|
||||
|
||||
/**
|
||||
* @see #toStaticHashString()
|
||||
* @see #getHash()
|
||||
* @return a boolean indicating if the two trees are isomorphics, defined has
|
||||
* having the same hash and the same hash serialization.
|
||||
*/
|
||||
boolean isIsomorphicTo(ITree tree);
|
||||
|
||||
/**
|
||||
* Indicate whether or not the tree is similar to the given tree.
|
||||
* @return true if they are compatible and have same label, false either
|
||||
*/
|
||||
boolean hasSameTypeAndLabel(ITree t);
|
||||
|
||||
/**
|
||||
* Refresh hash, size, depth and height of the tree.
|
||||
* @see com.github.gumtreediff.tree.hash.HashGenerator
|
||||
* @see TreeUtils#computeDepth(ITree)
|
||||
* @see TreeUtils#computeHeight(ITree)
|
||||
* @see TreeUtils#computeSize(ITree)
|
||||
*/
|
||||
void refresh();
|
||||
|
||||
String toStaticHashString();
|
||||
|
||||
String toShortString();
|
||||
|
||||
String toTreeString();
|
||||
|
||||
String toPrettyString(TreeContext ctx);
|
||||
|
||||
Object getMetadata(String key);
|
||||
|
||||
Object setMetadata(String key, Object value);
|
||||
|
||||
Iterator<Entry<String, Object>> getMetadata();
|
||||
|
||||
public abstract String getChildrenLabels();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.tree;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
|
||||
public abstract class IterableEnumeration<T> implements Iterable<T> {
|
||||
|
||||
public static <T> Iterable<T> make(Enumeration<T> en) {
|
||||
return new Iterable<T>() {
|
||||
public Iterator<T> iterator() {
|
||||
return new Iterator<T>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return en.hasMoreElements();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
return en.nextElement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.tree;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class Tree extends AbstractTree implements ITree {
|
||||
|
||||
private int type;
|
||||
|
||||
private String label;
|
||||
|
||||
// Begin position of the tree in terms of absolute character index and length
|
||||
private int pos;
|
||||
private int length;
|
||||
// End position
|
||||
|
||||
private AssociationMap metadata;
|
||||
|
||||
/**
|
||||
* Constructs a new node. If you need type labels corresponding to the integer
|
||||
* @see TreeContext#createTree(int, String, String)
|
||||
*/
|
||||
public Tree(int type, String label) {
|
||||
this.type = type;
|
||||
this.label = (label == null) ? NO_LABEL : label.intern();
|
||||
this.id = NO_ID;
|
||||
this.depth = NO_VALUE;
|
||||
this.hash = NO_VALUE;
|
||||
this.height = NO_VALUE;
|
||||
this.depth = NO_VALUE;
|
||||
this.size = NO_VALUE;
|
||||
this.pos = NO_VALUE;
|
||||
this.length = NO_VALUE;
|
||||
this.children = new ArrayList<>();
|
||||
}
|
||||
|
||||
|
||||
// Only used for cloning ...
|
||||
private Tree(Tree other) {
|
||||
this.type = other.type;
|
||||
this.label = other.getLabel();
|
||||
this.id = other.getId();
|
||||
this.pos = other.getPos();
|
||||
this.length = other.getLength();
|
||||
this.height = other.getHeight();
|
||||
this.size = other.getSize();
|
||||
this.depth = other.getDepth();
|
||||
this.hash = other.getHash();
|
||||
this.depth = other.getDepth();
|
||||
this.children = new ArrayList<>();
|
||||
this.metadata = other.metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChild(ITree t) {
|
||||
children.add(t);
|
||||
t.setParent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertChild(ITree t, int position) {
|
||||
children.add(position, t);
|
||||
t.setParent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tree deepCopy() {
|
||||
Tree copy = new Tree(this);
|
||||
for (ITree child : getChildren())
|
||||
copy.addChild(child.deepCopy());
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ITree> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITree getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChildren(List<ITree> children) {
|
||||
this.children = children;
|
||||
for (ITree c : children)
|
||||
c.setParent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLength(int length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParent(ITree parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParentAndUpdateChildren(ITree parent) {
|
||||
if (this.parent != null)
|
||||
this.parent.getChildren().remove(this);
|
||||
this.parent = parent;
|
||||
if (this.parent != null)
|
||||
parent.getChildren().add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPos(int pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getMetadata(String key) {
|
||||
if (metadata == null)
|
||||
return null;
|
||||
return metadata.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setMetadata(String key, Object value) {
|
||||
if (value == null) {
|
||||
if (metadata == null)
|
||||
return null;
|
||||
else
|
||||
return metadata.remove(key);
|
||||
}
|
||||
if (metadata == null)
|
||||
metadata = new AssociationMap();
|
||||
return metadata.set(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<String, Object>> getMetadata() {
|
||||
if (metadata == null)
|
||||
return new EmptyEntryIterator();
|
||||
return metadata.iterator();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.tree;
|
||||
|
||||
import com.github.gumtreediff.io.TreeIoUtils;
|
||||
import com.github.gumtreediff.io.TreeIoUtils.MetadataSerializer;
|
||||
import com.github.gumtreediff.io.TreeIoUtils.MetadataUnserializer;
|
||||
import com.github.gumtreediff.io.TreeIoUtils.TreeFormatter;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TreeContext {
|
||||
|
||||
private Map<Integer, String> typeLabels = new HashMap<>();
|
||||
|
||||
private final Map<String, Object> metadata = new HashMap<>();
|
||||
|
||||
private final MetadataSerializers serializers = new MetadataSerializers();
|
||||
|
||||
private ITree root;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return TreeIoUtils.toLisp(this).toString();
|
||||
}
|
||||
|
||||
public void setRoot(ITree root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
public ITree getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public String getTypeLabel(ITree tree) {
|
||||
return getTypeLabel(tree.getType());
|
||||
}
|
||||
|
||||
public String getTypeLabel(int type) {
|
||||
String tl = typeLabels.get(type);
|
||||
if (tl == null)
|
||||
tl = Integer.toString(type);
|
||||
return tl;
|
||||
}
|
||||
|
||||
protected void registerTypeLabel(int type, String name) {
|
||||
if (name == null || name.equals(ITree.NO_LABEL))
|
||||
return;
|
||||
String typeLabel = typeLabels.get(type);
|
||||
if (typeLabel == null)
|
||||
typeLabels.put(type, name);
|
||||
else if (!typeLabel.equals(name))
|
||||
throw new RuntimeException(String.format("Redefining type %d: '%s' with '%s'", type, typeLabel, name));
|
||||
}
|
||||
|
||||
public ITree createTree(int type, String label, String typeLabel) {
|
||||
registerTypeLabel(type, typeLabel);
|
||||
return new Tree(type, label);
|
||||
|
||||
}
|
||||
|
||||
public ITree createTree(ITree... trees) {
|
||||
return new AbstractTree.FakeTree(trees);
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
root.refresh();
|
||||
TreeUtils.postOrderNumbering(root);
|
||||
}
|
||||
|
||||
public boolean hasLabelFor(int type) {
|
||||
return typeLabels.containsKey(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a global metadata.
|
||||
* There is no way to know if the metadata is really null or does not exists.
|
||||
*
|
||||
* @param key of metadata
|
||||
* @return the metadata or null if not found
|
||||
*/
|
||||
public Object getMetadata(String key) {
|
||||
return metadata.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a local metadata, if available. Otherwise get a global metadata.
|
||||
* There is no way to know if the metadata is really null or does not exists.
|
||||
*
|
||||
* @param key of metadata
|
||||
* @return the metadata or null if not found
|
||||
*/
|
||||
public Object getMetadata(ITree node, String key) {
|
||||
Object metadata;
|
||||
if (node == null || (metadata = node.getMetadata(key)) == null)
|
||||
return getMetadata(key);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a global metadata.
|
||||
*
|
||||
* @param key of the metadata
|
||||
* @param value of the metadata
|
||||
* @return the previous value of metadata if existed or null
|
||||
*/
|
||||
public Object setMetadata(String key, Object value) {
|
||||
return metadata.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a local metadata
|
||||
*
|
||||
* @param key of the metadata
|
||||
* @param value of the metadata
|
||||
* @return the previous value of metadata if existed or null
|
||||
*/
|
||||
public Object setMetadata(ITree node, String key, Object value) {
|
||||
if (node == null)
|
||||
return setMetadata(key, value);
|
||||
else {
|
||||
Object res = node.setMetadata(key, value);
|
||||
if (res == null)
|
||||
return getMetadata(key);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an iterator on global metadata only
|
||||
*/
|
||||
public Iterator<Entry<String, Object>> getMetadata() {
|
||||
return metadata.entrySet().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get serializers for this tree context
|
||||
*/
|
||||
public MetadataSerializers getSerializers() {
|
||||
return serializers;
|
||||
}
|
||||
|
||||
public TreeContext export(MetadataSerializers s) {
|
||||
serializers.addAll(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TreeContext export(String key, MetadataSerializer s) {
|
||||
serializers.add(key, s);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TreeContext export(String... name) {
|
||||
for (String n : name)
|
||||
serializers.add(n, x -> x.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
public TreeContext deriveTree() { // FIXME Should we refactor TreeContext class to allow shared metadata etc ...
|
||||
TreeContext newContext = new TreeContext();
|
||||
newContext.setRoot(getRoot().deepCopy());
|
||||
newContext.typeLabels = typeLabels;
|
||||
newContext.metadata.putAll(metadata);
|
||||
newContext.serializers.addAll(serializers);
|
||||
return newContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an iterator on local and global metadata.
|
||||
* To only get local metadata, simply use : `node.getMetadata()`
|
||||
*/
|
||||
public Iterator<Entry<String, Object>> getMetadata(ITree node) {
|
||||
if (node == null)
|
||||
return getMetadata();
|
||||
return new Iterator<Entry<String, Object>>() {
|
||||
final Iterator<Entry<String, Object>> localIterator = node.getMetadata();
|
||||
final Iterator<Entry<String, Object>> globalIterator = getMetadata();
|
||||
final Set<String> seenKeys = new HashSet<>();
|
||||
|
||||
Iterator<Entry<String, Object>> currentIterator = localIterator;
|
||||
Entry<String, Object> nextEntry;
|
||||
|
||||
{
|
||||
next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return nextEntry != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<String, Object> next() {
|
||||
Entry<String, Object> n = nextEntry;
|
||||
if (currentIterator == localIterator) {
|
||||
if (localIterator.hasNext()) {
|
||||
nextEntry = localIterator.next();
|
||||
seenKeys.add(nextEntry.getKey());
|
||||
return n;
|
||||
} else {
|
||||
currentIterator = globalIterator;
|
||||
}
|
||||
}
|
||||
nextEntry = null;
|
||||
while (globalIterator.hasNext()) {
|
||||
Entry<String, Object> e = globalIterator.next();
|
||||
if (!(seenKeys.contains(e.getKey()) || (e.getValue() == null))) {
|
||||
nextEntry = e;
|
||||
seenKeys.add(nextEntry.getKey());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static class Marshallers<E> {
|
||||
Map<String, E> serializers = new HashMap<>();
|
||||
|
||||
public static final Pattern valid_id = Pattern.compile("[a-zA-Z0-9_]*");
|
||||
|
||||
public void addAll(Marshallers<E> other) {
|
||||
addAll(other.serializers);
|
||||
}
|
||||
|
||||
public void addAll(Map<String, E> serializers) {
|
||||
serializers.forEach((k, s) -> add(k, s));
|
||||
}
|
||||
|
||||
public void add(String name, E serializer) {
|
||||
if (!valid_id.matcher(name).matches()) // TODO I definitely don't like this rule, we should think twice
|
||||
throw new RuntimeException("Invalid key for serialization");
|
||||
serializers.put(name, serializer);
|
||||
}
|
||||
|
||||
public void remove(String key) {
|
||||
serializers.remove(key);
|
||||
}
|
||||
|
||||
public Set<String> exports() {
|
||||
return serializers.keySet();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MetadataSerializers extends Marshallers<MetadataSerializer> {
|
||||
|
||||
public void serialize(TreeFormatter formatter, String key, Object value) throws Exception {
|
||||
MetadataSerializer s = serializers.get(key);
|
||||
if (s != null)
|
||||
formatter.serializeAttribute(key, s.toString(value));
|
||||
}
|
||||
}
|
||||
|
||||
public static class MetadataUnserializers extends Marshallers<MetadataUnserializer> {
|
||||
|
||||
public void load(ITree tree, String key, String value) throws Exception {
|
||||
MetadataUnserializer s = serializers.get(key);
|
||||
if (s != null) {
|
||||
// if (key.equals("pos"))
|
||||
//// tree.setPos(Integer.parseInt(value));
|
||||
// else if (key.equals("length"))
|
||||
//// tree.setLength(Integer.parseInt(value));
|
||||
if (key.equals("line_before"))
|
||||
tree.setPos(Integer.parseInt(value));
|
||||
else if (key.equals("line_after"))
|
||||
tree.setLength(Integer.parseInt(value));
|
||||
else
|
||||
tree.setMetadata(key, s.fromString(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.tree;
|
||||
|
||||
import gnu.trove.map.TIntObjectMap;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
|
||||
public final class TreeMap {
|
||||
|
||||
private TIntObjectMap<ITree> trees;
|
||||
|
||||
public TreeMap(ITree tree) {
|
||||
this();
|
||||
putTrees(tree);
|
||||
}
|
||||
|
||||
public TreeMap() {
|
||||
trees = new TIntObjectHashMap<>();
|
||||
}
|
||||
|
||||
public ITree getTree(int id) {
|
||||
return trees.get(id);
|
||||
}
|
||||
|
||||
public boolean contains(ITree tree) {
|
||||
return contains(tree.getId());
|
||||
}
|
||||
|
||||
public boolean contains(int id) {
|
||||
return trees.containsKey(id);
|
||||
}
|
||||
|
||||
public void putTrees(ITree tree) {
|
||||
for (ITree t: tree.getTrees())
|
||||
trees.put(t.getId(), t);
|
||||
}
|
||||
|
||||
public void putTree(ITree t) {
|
||||
trees.put(t.getId(), t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.tree;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import com.github.gumtreediff.utils.Pair;
|
||||
|
||||
public final class TreeUtils {
|
||||
|
||||
private TreeUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the depth of every node of the tree. The size is set
|
||||
* directly on the nodes and is then accessible using {@link Tree#getSize()}.
|
||||
* @param tree a Tree
|
||||
*/
|
||||
public static void computeSize(ITree tree) {
|
||||
for (ITree t: tree.postOrder()) {
|
||||
int size = 1;
|
||||
if (!t.isLeaf())
|
||||
for (ITree c: t.getChildren())
|
||||
size += c.getSize();
|
||||
t.setSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the depth of every node of the tree. The depth is set
|
||||
* directly on the nodes and is then accessible using {@link Tree#getDepth()}.
|
||||
* @param tree a Tree
|
||||
*/
|
||||
public static void computeDepth(ITree tree) {
|
||||
List<ITree> trees = preOrder(tree);
|
||||
for (ITree t: trees) {
|
||||
int depth = 0;
|
||||
if (!t.isRoot()) depth = t.getParent().getDepth() + 1;
|
||||
t.setDepth(depth);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the height of every node of the tree. The height is set
|
||||
* directly on the nodes and is then accessible using {@link Tree#getHeight()}.
|
||||
* @param tree a Tree.
|
||||
*/
|
||||
public static void computeHeight(ITree tree) {
|
||||
for (ITree t: tree.postOrder()) {
|
||||
int height = 0;
|
||||
if (!t.isLeaf()) {
|
||||
for (ITree c: t.getChildren()) {
|
||||
int cHeight = c.getHeight();
|
||||
if (cHeight > height) height = cHeight;
|
||||
}
|
||||
height++;
|
||||
}
|
||||
t.setHeight(height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of every subtrees and the tree ordered using a pre-order.
|
||||
* @param tree a Tree.
|
||||
*/
|
||||
public static List<ITree> preOrder(ITree tree) {
|
||||
List<ITree> trees = new ArrayList<>();
|
||||
preOrder(tree, trees);
|
||||
return trees;
|
||||
}
|
||||
|
||||
private static void preOrder(ITree tree, List<ITree> trees) {
|
||||
trees.add(tree);
|
||||
if (!tree.isLeaf())
|
||||
for (ITree c: tree.getChildren())
|
||||
preOrder(c, trees);
|
||||
}
|
||||
|
||||
public static void preOrderNumbering(ITree tree) {
|
||||
numbering(tree.preOrder());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of every subtrees and the tree ordered using a breadth-first order.
|
||||
* @param tree a Tree.
|
||||
*/
|
||||
public static List<ITree> breadthFirst(ITree tree) {
|
||||
List<ITree> trees = new ArrayList<>();
|
||||
List<ITree> currents = new ArrayList<>();
|
||||
currents.add(tree);
|
||||
while (currents.size() > 0) {
|
||||
ITree c = currents.remove(0);
|
||||
trees.add(c);
|
||||
currents.addAll(c.getChildren());
|
||||
}
|
||||
return trees;
|
||||
}
|
||||
|
||||
public static Iterator<ITree> breadthFirstIterator(final ITree tree) {
|
||||
return new Iterator<ITree>() {
|
||||
Deque<Iterator<ITree>> fifo = new ArrayDeque<>();
|
||||
|
||||
{
|
||||
addLasts(new AbstractTree.FakeTree(tree));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !fifo.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITree next() {
|
||||
while (!fifo.isEmpty()) {
|
||||
Iterator<ITree> it = fifo.getFirst();
|
||||
if (it.hasNext()) {
|
||||
ITree item = it.next();
|
||||
if (!it.hasNext())
|
||||
fifo.removeFirst();
|
||||
addLasts(item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
private void addLasts(ITree item) {
|
||||
List<ITree> children = item.getChildren();
|
||||
if (!children.isEmpty())
|
||||
fifo.addLast(children.iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new RuntimeException("Not yet implemented implemented.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void breadthFirstNumbering(ITree tree) {
|
||||
numbering(tree.breadthFirst());
|
||||
}
|
||||
|
||||
public static void numbering(Iterable<ITree> iterable) {
|
||||
int i = 0;
|
||||
for (ITree t: iterable)
|
||||
t.setId(i++);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of every subtrees and the tree ordered using a post-order.
|
||||
* @param tree a Tree.
|
||||
*/
|
||||
public static List<ITree> postOrder(ITree tree) {
|
||||
List<ITree> trees = new ArrayList<>();
|
||||
postOrder(tree, trees);
|
||||
return trees;
|
||||
}
|
||||
|
||||
private static void postOrder(ITree tree, List<ITree> trees) {
|
||||
if (!tree.isLeaf())
|
||||
for (ITree c: tree.getChildren())
|
||||
postOrder(c, trees);
|
||||
trees.add(tree);
|
||||
}
|
||||
|
||||
public static Iterator<ITree> postOrderIterator(final ITree tree) {
|
||||
return new Iterator<ITree>() {
|
||||
Deque<Pair<ITree, Iterator<ITree>>> stack = new ArrayDeque<>();
|
||||
{
|
||||
push(tree);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return stack.size() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITree next() {
|
||||
if (stack.isEmpty())
|
||||
throw new NoSuchElementException();
|
||||
return selectNextChild(stack.peek().getSecond());
|
||||
}
|
||||
|
||||
ITree selectNextChild(Iterator<ITree> it) {
|
||||
if (!it.hasNext())
|
||||
return stack.pop().getFirst();
|
||||
ITree item = it.next();
|
||||
if (item.isLeaf())
|
||||
return item;
|
||||
return selectNextChild(push(item));
|
||||
}
|
||||
|
||||
private Iterator<ITree> push(ITree item) {
|
||||
Iterator<ITree> it = item.getChildren().iterator();
|
||||
stack.push(new Pair<>(item, it));
|
||||
return it;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new RuntimeException("Not yet implemented implemented.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void visitTree(ITree root, TreeVisitor visitor) {
|
||||
Deque<Pair<ITree, Iterator<ITree>>> stack = new ArrayDeque<>();
|
||||
stack.push(new Pair<>(root, root.getChildren().iterator()));
|
||||
visitor.startTree(root);
|
||||
while (!stack.isEmpty()) {
|
||||
Pair<ITree, Iterator<ITree>> it = stack.peek();
|
||||
|
||||
if (!it.second.hasNext()) {
|
||||
visitor.endTree(it.first);
|
||||
stack.pop();
|
||||
} else {
|
||||
ITree child = it.second.next();
|
||||
stack.push(new Pair<>(child, child.getChildren().iterator()));
|
||||
visitor.startTree(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface TreeVisitor {
|
||||
void startTree(ITree tree);
|
||||
|
||||
void endTree(ITree tree);
|
||||
}
|
||||
|
||||
public static Iterator<ITree> preOrderIterator(ITree tree) {
|
||||
return new Iterator<ITree>() {
|
||||
Deque<Iterator<ITree>> stack = new ArrayDeque<>();
|
||||
{
|
||||
push(new AbstractTree.FakeTree(tree));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return stack.size() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITree next() {
|
||||
Iterator<ITree> it = stack.peek();
|
||||
if (it == null)
|
||||
throw new NoSuchElementException();
|
||||
ITree t = it.next();
|
||||
while (it != null && !it.hasNext()) {
|
||||
stack.pop();
|
||||
it = stack.peek();
|
||||
}
|
||||
push(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
private void push(ITree tree) {
|
||||
if (!tree.isLeaf())
|
||||
stack.push(tree.getChildren().iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new RuntimeException("Not yet implemented implemented.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Iterator<ITree> leafIterator(final Iterator<ITree> it) {
|
||||
return new Iterator<ITree>() {
|
||||
ITree current = it.hasNext() ? it.next() : null;
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return current != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITree next() {
|
||||
ITree val = current;
|
||||
while (it.hasNext()) {
|
||||
current = it.next();
|
||||
if (current.isLeaf())
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new RuntimeException("Not yet implemented implemented.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void postOrderNumbering(ITree tree) {
|
||||
numbering(tree.postOrder());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.tree.hash;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
public interface HashGenerator {
|
||||
|
||||
public void hash(ITree t);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.tree.hash;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
public class HashUtils {
|
||||
|
||||
private HashUtils() {}
|
||||
|
||||
public static final int BASE = 33;
|
||||
|
||||
public static final HashGenerator DEFAULT_HASH_GENERATOR = new RollingHashGenerator.Md5RollingHashGenerator();
|
||||
|
||||
public static int byteArrayToInt(byte[] b) {
|
||||
return b[3] & 0xFF | (b[2] & 0xFF) << 8 | (b[1] & 0xFF) << 16 | (b[0] & 0xFF) << 24;
|
||||
}
|
||||
|
||||
public static int standardHash(ITree t) {
|
||||
return Integer.hashCode(t.getType()) + HashUtils.BASE * t.getLabel().hashCode();
|
||||
}
|
||||
|
||||
public static String inSeed(ITree t) {
|
||||
return ITree.OPEN_SYMBOL + t.getLabel() + ITree.SEPARATE_SYMBOL + t.getType();
|
||||
}
|
||||
|
||||
public static String outSeed(ITree t) {
|
||||
return t.getType() + ITree.SEPARATE_SYMBOL + t.getLabel() + ITree.CLOSE_SYMBOL;
|
||||
}
|
||||
|
||||
public static int md5(String s) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] digest = md.digest(s.getBytes());
|
||||
return byteArrayToInt(digest);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return ITree.NO_VALUE;
|
||||
}
|
||||
|
||||
public static int fpow(int a, int b) {
|
||||
if (b == 1)
|
||||
return a;
|
||||
int result = 1;
|
||||
while (b > 0) {
|
||||
if ((b & 1) != 0)
|
||||
result *= a;
|
||||
b >>= 1;
|
||||
a *= a;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.tree.hash;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import static com.github.gumtreediff.tree.hash.HashUtils.*;
|
||||
|
||||
public abstract class RollingHashGenerator implements HashGenerator {
|
||||
|
||||
public void hash(ITree t) {
|
||||
for (ITree n: t.postOrder())
|
||||
if (n.isLeaf())
|
||||
n.setHash(leafHash(n));
|
||||
else
|
||||
n.setHash(innerNodeHash(n));
|
||||
}
|
||||
|
||||
public abstract int hashFunction(String s);
|
||||
|
||||
public int leafHash(ITree t) {
|
||||
return BASE * hashFunction(HashUtils.inSeed(t)) + hashFunction(HashUtils.outSeed(t));
|
||||
}
|
||||
|
||||
public int innerNodeHash(ITree t) {
|
||||
int size = t.getSize() * 2 - 1;
|
||||
int hash = hashFunction(HashUtils.inSeed(t)) * fpow(BASE, size);
|
||||
|
||||
for (ITree c: t.getChildren()) {
|
||||
size = size - c.getSize() * 2;
|
||||
hash += c.getHash() * fpow(BASE, size);
|
||||
}
|
||||
|
||||
hash += hashFunction(HashUtils.outSeed(t));
|
||||
return hash;
|
||||
}
|
||||
|
||||
public static class JavaRollingHashGenerator extends RollingHashGenerator {
|
||||
|
||||
@Override
|
||||
public int hashFunction(String s) {
|
||||
return s.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Md5RollingHashGenerator extends RollingHashGenerator {
|
||||
|
||||
@Override
|
||||
public int hashFunction(String s) {
|
||||
return md5(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class RandomRollingHashGenerator extends RollingHashGenerator {
|
||||
|
||||
private static final Map<String, Integer> digests = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public int hashFunction(String s) {
|
||||
return rdmHash(s);
|
||||
}
|
||||
|
||||
public static int rdmHash(String s) {
|
||||
if (!digests.containsKey(s)) {
|
||||
int digest = (int) (Math.random() * (Integer.MAX_VALUE - 1));
|
||||
digests.put(s, digest);
|
||||
return digest;
|
||||
} else return digests.get(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.tree.hash;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
public abstract class StaticHashGenerator implements HashGenerator {
|
||||
|
||||
public void hash(ITree t) {
|
||||
for (ITree n: t.postOrder())
|
||||
n.setHash(nodeHash(n));
|
||||
}
|
||||
|
||||
public abstract int nodeHash(ITree t);
|
||||
|
||||
public static class StdHashGenerator extends StaticHashGenerator {
|
||||
|
||||
@Override
|
||||
public int nodeHash(ITree t) {
|
||||
return t.toStaticHashString().hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Md5HashGenerator extends StaticHashGenerator {
|
||||
|
||||
@Override
|
||||
public int nodeHash(ITree t) {
|
||||
return HashUtils.md5(t.toStaticHashString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
/* Copyright (c) 2012 Kevin L. Stern
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.utils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* An implementation of the Hungarian algorithm for solving the assignment
|
||||
* problem. An instance of the assignment problem consists of a number of
|
||||
* workers along with a number of jobs and a cost matrix which gives the cost of
|
||||
* assigning the i'th worker to the j'th job at position (i, j). The goal is to
|
||||
* find an assignment of workers to jobs so that no job is assigned more than
|
||||
* one worker and so that no worker is assigned to more than one job in such a
|
||||
* manner so as to minimize the total cost of completing the jobs.
|
||||
* An assignment for a cost matrix that has more workers than jobs will
|
||||
* necessarily include unassigned workers, indicated by an assignment value of
|
||||
* -1; in no other circumstance will there be unassigned workers. Similarly, an
|
||||
* assignment for a cost matrix that has more jobs than workers will necessarily
|
||||
* include unassigned jobs; in no other circumstance will there be unassigned
|
||||
* jobs. For completeness, an assignment for a square cost matrix will give
|
||||
* exactly one unique worker to each job.
|
||||
|
||||
* This version of the Hungarian algorithm runs in time O(n^3), where n is the
|
||||
* maximum among the number of workers and the number of jobs.
|
||||
*
|
||||
* @author Kevin L. Stern
|
||||
*/
|
||||
public class HungarianAlgorithm {
|
||||
private final double[][] costMatrix;
|
||||
private final int rows;
|
||||
private final int cols;
|
||||
private final int dim;
|
||||
private final double[] labelByWorker;
|
||||
private final double[] labelByJob;
|
||||
private final int[] minSlackWorkerByJob;
|
||||
private final double[] minSlackValueByJob;
|
||||
private final int[] matchJobByWorker;
|
||||
private final int[] matchWorkerByJob;
|
||||
private final int[] parentWorkerByCommittedJob;
|
||||
private final boolean[] committedWorkers;
|
||||
|
||||
/**
|
||||
* Construct an instance of the algorithm.
|
||||
*
|
||||
* @param costMatrix
|
||||
* the cost matrix, where matrix[i][j] holds the cost of
|
||||
* assigning worker i to job j, for all i, j. The cost matrix
|
||||
* must not be irregular in the sense that all rows must be the
|
||||
* same length.
|
||||
*/
|
||||
public HungarianAlgorithm(double[][] costMatrix) {
|
||||
this.dim = Math.max(costMatrix.length, costMatrix[0].length);
|
||||
this.rows = costMatrix.length;
|
||||
this.cols = costMatrix[0].length;
|
||||
this.costMatrix = new double[this.dim][this.dim];
|
||||
for (int w = 0; w < this.dim; w++) {
|
||||
if (w < costMatrix.length) {
|
||||
if (costMatrix[w].length != this.cols) {
|
||||
throw new IllegalArgumentException("Irregular cost matrix");
|
||||
}
|
||||
this.costMatrix[w] = Arrays.copyOf(costMatrix[w], this.dim);
|
||||
} else {
|
||||
this.costMatrix[w] = new double[this.dim];
|
||||
}
|
||||
}
|
||||
labelByWorker = new double[this.dim];
|
||||
labelByJob = new double[this.dim];
|
||||
minSlackWorkerByJob = new int[this.dim];
|
||||
minSlackValueByJob = new double[this.dim];
|
||||
committedWorkers = new boolean[this.dim];
|
||||
parentWorkerByCommittedJob = new int[this.dim];
|
||||
matchJobByWorker = new int[this.dim];
|
||||
Arrays.fill(matchJobByWorker, -1);
|
||||
matchWorkerByJob = new int[this.dim];
|
||||
Arrays.fill(matchWorkerByJob, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute an initial feasible solution by assigning zero labels to the
|
||||
* workers and by assigning to each job a label equal to the minimum cost
|
||||
* among its incident edges.
|
||||
*/
|
||||
protected void computeInitialFeasibleSolution() {
|
||||
for (int j = 0; j < dim; j++) {
|
||||
labelByJob[j] = Double.POSITIVE_INFINITY;
|
||||
}
|
||||
for (int w = 0; w < dim; w++) {
|
||||
for (int j = 0; j < dim; j++) {
|
||||
if (costMatrix[w][j] < labelByJob[j]) {
|
||||
labelByJob[j] = costMatrix[w][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the algorithm.
|
||||
*
|
||||
* @return the minimum cost matching of workers to jobs based upon the
|
||||
* provided cost matrix. A matching value of -1 indicates that the
|
||||
* corresponding worker is unassigned.
|
||||
*/
|
||||
public int[] execute() {
|
||||
/*
|
||||
* Heuristics to improve performance: Reduce rows and columns by their
|
||||
* smallest element, compute an initial non-zero dual feasible solution
|
||||
* and create a greedy matching from workers to jobs of the cost matrix.
|
||||
*/
|
||||
reduce();
|
||||
computeInitialFeasibleSolution();
|
||||
greedyMatch();
|
||||
|
||||
int w = fetchUnmatchedWorker();
|
||||
while (w < dim) {
|
||||
initializePhase(w);
|
||||
executePhase();
|
||||
w = fetchUnmatchedWorker();
|
||||
}
|
||||
int[] result = Arrays.copyOf(matchJobByWorker, rows);
|
||||
for (w = 0; w < result.length; w++) {
|
||||
if (result[w] >= cols) {
|
||||
result[w] = -1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a single phase of the algorithm. A phase of the Hungarian
|
||||
* algorithm consists of building a set of committed workers and a set of
|
||||
* committed jobs from a root unmatched worker by following alternating
|
||||
* unmatched/matched zero-slack edges. If an unmatched job is encountered,
|
||||
* then an augmenting path has been found and the matching is grown. If the
|
||||
* connected zero-slack edges have been exhausted, the labels of committed
|
||||
* workers are increased by the minimum slack among committed workers and
|
||||
* non-committed jobs to create more zero-slack edges (the labels of
|
||||
* committed jobs are simultaneously decreased by the same amount in order
|
||||
* to maintain a feasible labeling).
|
||||
* The runtime of a single phase of the algorithm is O(n^2), where n is the
|
||||
* dimension of the internal square cost matrix, since each edge is visited
|
||||
* at most once and since increasing the labeling is accomplished in time
|
||||
* O(n) by maintaining the minimum slack values among non-committed jobs.
|
||||
* When a phase completes, the matching will have increased in size.
|
||||
*/
|
||||
protected void executePhase() {
|
||||
while (true) {
|
||||
int minSlackWorker = -1, minSlackJob = -1;
|
||||
double minSlackValue = Double.POSITIVE_INFINITY;
|
||||
for (int j = 0; j < dim; j++) {
|
||||
if (parentWorkerByCommittedJob[j] == -1) {
|
||||
if (minSlackValueByJob[j] < minSlackValue) {
|
||||
minSlackValue = minSlackValueByJob[j];
|
||||
minSlackWorker = minSlackWorkerByJob[j];
|
||||
minSlackJob = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (minSlackValue > 0) {
|
||||
updateLabeling(minSlackValue);
|
||||
}
|
||||
parentWorkerByCommittedJob[minSlackJob] = minSlackWorker;
|
||||
if (matchWorkerByJob[minSlackJob] == -1) {
|
||||
int committedJob = minSlackJob;
|
||||
int parentWorker = parentWorkerByCommittedJob[committedJob];
|
||||
while (true) {
|
||||
int temp = matchJobByWorker[parentWorker];
|
||||
match(parentWorker, committedJob);
|
||||
committedJob = temp;
|
||||
if (committedJob == -1) {
|
||||
break;
|
||||
}
|
||||
parentWorker = parentWorkerByCommittedJob[committedJob];
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
int worker = matchWorkerByJob[minSlackJob];
|
||||
committedWorkers[worker] = true;
|
||||
for (int j = 0; j < dim; j++) {
|
||||
if (parentWorkerByCommittedJob[j] == -1) {
|
||||
double slack = costMatrix[worker][j]
|
||||
- labelByWorker[worker] - labelByJob[j];
|
||||
if (minSlackValueByJob[j] > slack) {
|
||||
minSlackValueByJob[j] = slack;
|
||||
minSlackWorkerByJob[j] = worker;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @return the first unmatched worker or {@link #dim} if none.
|
||||
*/
|
||||
protected int fetchUnmatchedWorker() {
|
||||
int w;
|
||||
for (w = 0; w < dim; w++) {
|
||||
if (matchJobByWorker[w] == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a valid matching by greedily selecting among zero-cost matchings.
|
||||
* This is a heuristic to jump-start the augmentation algorithm.
|
||||
*/
|
||||
protected void greedyMatch() {
|
||||
for (int w = 0; w < dim; w++) {
|
||||
for (int j = 0; j < dim; j++) {
|
||||
if (matchJobByWorker[w] == -1
|
||||
&& matchWorkerByJob[j] == -1
|
||||
&& costMatrix[w][j] - labelByWorker[w] - labelByJob[j] == 0) {
|
||||
match(w, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the next phase of the algorithm by clearing the committed
|
||||
* workers and jobs sets and by initializing the slack arrays to the values
|
||||
* corresponding to the specified root worker.
|
||||
*
|
||||
* @param w
|
||||
* the worker at which to root the next phase.
|
||||
*/
|
||||
protected void initializePhase(int w) {
|
||||
Arrays.fill(committedWorkers, false);
|
||||
Arrays.fill(parentWorkerByCommittedJob, -1);
|
||||
committedWorkers[w] = true;
|
||||
for (int j = 0; j < dim; j++) {
|
||||
minSlackValueByJob[j] = costMatrix[w][j] - labelByWorker[w]
|
||||
- labelByJob[j];
|
||||
minSlackWorkerByJob[j] = w;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to record a matching between worker w and job j.
|
||||
*/
|
||||
protected void match(int w, int j) {
|
||||
matchJobByWorker[w] = j;
|
||||
matchWorkerByJob[j] = w;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the cost matrix by subtracting the smallest element of each row
|
||||
* from all elements of the row as well as the smallest element of each
|
||||
* column from all elements of the column. Note that an optimal assignment
|
||||
* for a reduced cost matrix is optimal for the original cost matrix.
|
||||
*/
|
||||
protected void reduce() {
|
||||
for (int w = 0; w < dim; w++) {
|
||||
double min = Double.POSITIVE_INFINITY;
|
||||
for (int j = 0; j < dim; j++) {
|
||||
if (costMatrix[w][j] < min) {
|
||||
min = costMatrix[w][j];
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < dim; j++) {
|
||||
costMatrix[w][j] -= min;
|
||||
}
|
||||
}
|
||||
double[] min = new double[dim];
|
||||
for (int j = 0; j < dim; j++) {
|
||||
min[j] = Double.POSITIVE_INFINITY;
|
||||
}
|
||||
for (int w = 0; w < dim; w++) {
|
||||
for (int j = 0; j < dim; j++) {
|
||||
if (costMatrix[w][j] < min[j]) {
|
||||
min[j] = costMatrix[w][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int w = 0; w < dim; w++) {
|
||||
for (int j = 0; j < dim; j++) {
|
||||
costMatrix[w][j] -= min[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update labels with the specified slack by adding the slack value for
|
||||
* committed workers and by subtracting the slack value for committed jobs.
|
||||
* In addition, update the minimum slack values appropriately.
|
||||
*/
|
||||
protected void updateLabeling(double slack) {
|
||||
for (int w = 0; w < dim; w++) {
|
||||
if (committedWorkers[w]) {
|
||||
labelByWorker[w] += slack;
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < dim; j++) {
|
||||
if (parentWorkerByCommittedJob[j] != -1) {
|
||||
labelByJob[j] -= slack;
|
||||
} else {
|
||||
minSlackValueByJob[j] -= slack;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.utils;
|
||||
|
||||
public class Pair<T1, T2> {
|
||||
|
||||
public final T1 first;
|
||||
|
||||
public final T2 second;
|
||||
|
||||
public Pair(T1 a, T2 b) {
|
||||
this.first = a;
|
||||
this.second = b;
|
||||
}
|
||||
|
||||
public T1 getFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public T2 getSecond() {
|
||||
return second;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Pair<?, ?> pair = (Pair<?, ?>) o;
|
||||
|
||||
if (!first.equals(pair.first)) return false;
|
||||
return second.equals(pair.second);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = first.hashCode();
|
||||
result = 31 * result + second.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + getFirst().toString() + "," + getSecond().toString() + ")";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
|
||||
public final class StringAlgorithms {
|
||||
|
||||
private StringAlgorithms() {}
|
||||
|
||||
public static List<int[]> lcss(String s0, String s1) {
|
||||
int[][] lengths = new int[s0.length() + 1][s1.length() + 1];
|
||||
for (int i = 0; i < s0.length(); i++)
|
||||
for (int j = 0; j < s1.length(); j++)
|
||||
if (s0.charAt(i) == (s1.charAt(j)))
|
||||
lengths[i + 1][j + 1] = lengths[i][j] + 1;
|
||||
else
|
||||
lengths[i + 1][j + 1] = Math.max(lengths[i + 1][j], lengths[i][j + 1]);
|
||||
|
||||
List<int[]> indexes = new ArrayList<>();
|
||||
|
||||
for (int x = s0.length(), y = s1.length(); x != 0 && y != 0; ) {
|
||||
if (lengths[x][y] == lengths[x - 1][y]) x--;
|
||||
else if (lengths[x][y] == lengths[x][y - 1]) y--;
|
||||
else {
|
||||
indexes.add(new int[] {x - 1, y - 1});
|
||||
x--;
|
||||
y--;
|
||||
}
|
||||
}
|
||||
Collections.reverse(indexes);
|
||||
return indexes;
|
||||
}
|
||||
|
||||
public static List<int[]> hunks(String s0, String s1) {
|
||||
List<int[]> lcs = lcss(s0 ,s1);
|
||||
List<int[]> hunks = new ArrayList<int[]>();
|
||||
int inf0 = -1;
|
||||
int inf1 = -1;
|
||||
int last0 = -1;
|
||||
int last1 = -1;
|
||||
for (int i = 0; i < lcs.size(); i++) {
|
||||
int[] match = lcs.get(i);
|
||||
if (inf0 == -1 || inf1 == -1) {
|
||||
inf0 = match[0];
|
||||
inf1 = match[1];
|
||||
} else if (last0 + 1 != match[0] || last1 + 1 != match[1]) {
|
||||
hunks.add(new int[] {inf0, last0 + 1, inf1, last1 + 1});
|
||||
inf0 = match[0];
|
||||
inf1 = match[1];
|
||||
} else if (i == lcs.size() - 1) {
|
||||
hunks.add(new int[] {inf0, match[0] + 1, inf1, match[1] + 1});
|
||||
break;
|
||||
}
|
||||
last0 = match[0];
|
||||
last1 = match[1];
|
||||
}
|
||||
return hunks;
|
||||
}
|
||||
|
||||
public static String lcs(String s1, String s2) {
|
||||
int start = 0;
|
||||
int max = 0;
|
||||
for (int i = 0; i < s1.length(); i++) {
|
||||
for (int j = 0; j < s2.length(); j++) {
|
||||
int x = 0;
|
||||
while (s1.charAt(i + x) == s2.charAt(j + x)) {
|
||||
x++;
|
||||
if (((i + x) >= s1.length()) || ((j + x) >= s2.length())) break;
|
||||
}
|
||||
if (x > max) {
|
||||
max = x;
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return s1.substring(start, (start + max));
|
||||
}
|
||||
|
||||
public static List<int[]> lcss(List<ITree> s0, List<ITree> s1) {
|
||||
int[][] lengths = new int[s0.size() + 1][s1.size() + 1];
|
||||
for (int i = 0; i < s0.size(); i++)
|
||||
for (int j = 0; j < s1.size(); j++)
|
||||
if (s0.get(i).hasSameTypeAndLabel(s1.get(j)))
|
||||
lengths[i + 1][j + 1] = lengths[i][j] + 1;
|
||||
else
|
||||
lengths[i + 1][j + 1] = Math.max(lengths[i + 1][j], lengths[i][j + 1]);
|
||||
|
||||
List<int[]> indexes = new ArrayList<>();
|
||||
|
||||
for (int x = s0.size(), y = s1.size(); x != 0 && y != 0; ) {
|
||||
if (lengths[x][y] == lengths[x - 1][y]) x--;
|
||||
else if (lengths[x][y] == lengths[x][y - 1]) y--;
|
||||
else {
|
||||
indexes.add(new int[] {x - 1, y - 1});
|
||||
x--;
|
||||
y--;
|
||||
}
|
||||
}
|
||||
Collections.reverse(indexes);
|
||||
return indexes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.test;
|
||||
|
||||
import com.github.gumtreediff.actions.ActionGenerator;
|
||||
import com.github.gumtreediff.actions.model.*;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.utils.Pair;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TestActionGenerator {
|
||||
|
||||
@Test
|
||||
public void testWithActionExample() {
|
||||
Pair<TreeContext, TreeContext> trees = TreeLoader.getActionPair();
|
||||
ITree src = trees.getFirst().getRoot();
|
||||
ITree dst = trees.getSecond().getRoot();
|
||||
MappingStore ms = new MappingStore();
|
||||
ms.link(src, dst);
|
||||
ms.link(src.getChild(1), dst.getChild(0));
|
||||
ms.link(src.getChild(1).getChild(0), dst.getChild(0).getChild(0));
|
||||
ms.link(src.getChild(1).getChild(1), dst.getChild(0).getChild(1));
|
||||
ms.link(src.getChild(0), dst.getChild(1).getChild(0));
|
||||
ms.link(src.getChild(0).getChild(0), dst.getChild(1).getChild(0).getChild(0));
|
||||
|
||||
ActionGenerator ag = new ActionGenerator(src, dst, ms);
|
||||
ag.generate();
|
||||
List<Action> actions = ag.getActions();
|
||||
|
||||
assertEquals(4, actions.size());
|
||||
Action a1 = actions.get(0);
|
||||
assertTrue(a1 instanceof Insert);
|
||||
Insert i = (Insert) a1;
|
||||
assertEquals("1@@h", i.getNode().toShortString());
|
||||
assertEquals("0@@a", i.getParent().toShortString());
|
||||
assertEquals(2, i.getPosition());
|
||||
Action a2 = actions.get(1);
|
||||
assertTrue(a2 instanceof Move);
|
||||
Move m = (Move) a2;
|
||||
assertEquals("0@@e", m.getNode().toShortString());
|
||||
assertEquals("1@@h", m.getParent().toShortString());
|
||||
assertEquals(0, m.getPosition());
|
||||
Action a3 = actions.get(2);
|
||||
assertTrue(a3 instanceof Update);
|
||||
Update u = (Update) a3;
|
||||
assertEquals("0@@f", u.getNode().toShortString());
|
||||
assertEquals("y", u.getValue());
|
||||
Action a4 = actions.get(3);
|
||||
assertTrue(a4 instanceof Delete);
|
||||
assertEquals("0@@g", a4.getNode().toShortString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithZsCustomExample() {
|
||||
Pair<TreeContext, TreeContext> trees = TreeLoader.getZsCustomPair();
|
||||
ITree src = trees.getFirst().getRoot();
|
||||
ITree dst = trees.getSecond().getRoot();
|
||||
MappingStore ms = new MappingStore();
|
||||
ms.link(src, dst.getChild(0));
|
||||
ms.link(src.getChild(0), dst.getChild(0).getChild(0));
|
||||
ms.link(src.getChild(1), dst.getChild(0).getChild(1));
|
||||
ms.link(src.getChild(1).getChild(0), dst.getChild(0).getChild(1).getChild(0));
|
||||
ms.link(src.getChild(1).getChild(2), dst.getChild(0).getChild(1).getChild(2));
|
||||
|
||||
ActionGenerator ag = new ActionGenerator(src, dst, ms);
|
||||
ag.generate();
|
||||
List<Action> actions = ag.getActions();
|
||||
System.out.println(actions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.test;
|
||||
|
||||
import com.github.gumtreediff.actions.ActionGenerator;
|
||||
import com.github.gumtreediff.actions.model.Action;
|
||||
import com.github.gumtreediff.io.ActionsIoUtils;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.matchers.Matchers;
|
||||
import com.github.gumtreediff.utils.Pair;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class TestActionIo {
|
||||
private TreeContext src;
|
||||
private TreeContext dst;
|
||||
private MappingStore mappings;
|
||||
private List<Action> actions;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Pair<TreeContext, TreeContext> p = TreeLoader.getActionPair();
|
||||
src = p.getFirst();
|
||||
dst = p.getSecond();
|
||||
Matcher m = Matchers.getInstance().getMatcher(src.getRoot(), dst.getRoot());
|
||||
mappings = m.getMappings();
|
||||
actions = new ActionGenerator(src.getRoot(), dst.getRoot(), mappings).generate();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPos() throws Exception {
|
||||
src.getRoot().breadthFirst().forEach(
|
||||
x -> System.out.printf("%d) %s [%d:%d]:%d\n",
|
||||
x.getType(), x.getLabel(), x.getPos(), x.getEndPos(), x.getLength()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicXmlActions() throws IOException {
|
||||
System.out.println(ActionsIoUtils.toXml(src, actions, mappings));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicTextActions() throws IOException {
|
||||
System.out.println(ActionsIoUtils.toText(src, actions, mappings));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicJsonActions() throws IOException {
|
||||
System.out.println(ActionsIoUtils.toJson(src, actions, mappings));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2016 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.test;
|
||||
|
||||
import com.github.gumtreediff.utils.HungarianAlgorithm;
|
||||
import com.github.gumtreediff.utils.StringAlgorithms;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TestAlgorithms {
|
||||
|
||||
@Test
|
||||
public void testLcss() {
|
||||
// Exemple coming from:
|
||||
// http://www.geeksforgeeks.org/dynamic-programming-set-4-longest-common-subsequence/
|
||||
List<int[]> indexes = StringAlgorithms.lcss("ABCDGH", "AEDFHR");
|
||||
assertThat(indexes.size(), is(3));
|
||||
assertThat(indexes, hasItem(new int[] {0, 0}));
|
||||
assertThat(indexes, hasItem(new int[] {3, 2}));
|
||||
assertThat(indexes, hasItem(new int[] {5, 4}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLcs() {
|
||||
String lcs = StringAlgorithms.lcs("FUTUR", "CHUTE");
|
||||
assertThat(lcs, is("UT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHungarianAlgorithm() {
|
||||
// Exemple coming from https://en.wikipedia.org/wiki/Hungarian_algorithm
|
||||
double[][] costMatrix = new double[3][];
|
||||
costMatrix[0] = new double[] {2F, 3F, 3F};
|
||||
costMatrix[1] = new double[] {3F, 2F, 3F};
|
||||
costMatrix[2] = new double[] {3F, 3F, 2F};
|
||||
HungarianAlgorithm a = new HungarianAlgorithm(costMatrix);
|
||||
int[] result = a.execute();
|
||||
assertThat(result[0], is(0));
|
||||
assertThat(result[1], is(1));
|
||||
assertThat(result[2], is(2));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
@RunWith(Suite.class)
|
||||
@Suite.SuiteClasses({
|
||||
TestTree.class,
|
||||
TestTreeUtils.class,
|
||||
TestTreeIoUtils.class,
|
||||
TestHash.class,
|
||||
TestZsMatcher.class,
|
||||
TestRtedMatcher.class })
|
||||
public class TestCoreSuite {}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2016 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2016 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.test;
|
||||
|
||||
import com.github.gumtreediff.matchers.CompositeMatchers;
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.matchers.heuristic.gt.AbstractBottomUpMatcher;
|
||||
import com.github.gumtreediff.matchers.heuristic.gt.GreedySubtreeMatcher;
|
||||
import com.github.gumtreediff.utils.Pair;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class TestGumtreeMatcher {
|
||||
|
||||
@Test
|
||||
public void testMinHeightThreshold() {
|
||||
Pair<TreeContext, TreeContext> trees = TreeLoader.getGumtreePair();
|
||||
GreedySubtreeMatcher.MIN_HEIGHT = 0;
|
||||
AbstractBottomUpMatcher.SIZE_THRESHOLD = 0;
|
||||
Matcher m = new CompositeMatchers.ClassicGumtree(
|
||||
trees.getFirst().getRoot(), trees.getSecond().getRoot(), new MappingStore());
|
||||
m.match();
|
||||
assertEquals(5, m.getMappingSet().size());
|
||||
GreedySubtreeMatcher.MIN_HEIGHT = 1;
|
||||
AbstractBottomUpMatcher.SIZE_THRESHOLD = 0;
|
||||
m = new CompositeMatchers.ClassicGumtree(
|
||||
trees.getFirst().getRoot(), trees.getSecond().getRoot(), new MappingStore());
|
||||
m.match();
|
||||
assertEquals(4, m.getMappingSet().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeThreshold() {
|
||||
Pair<TreeContext, TreeContext> trees = TreeLoader.getGumtreePair();
|
||||
GreedySubtreeMatcher.MIN_HEIGHT = 0;
|
||||
AbstractBottomUpMatcher.SIZE_THRESHOLD = 0;
|
||||
Matcher m = new CompositeMatchers.ClassicGumtree(
|
||||
trees.getFirst().getRoot(), trees.getSecond().getRoot(), new MappingStore());
|
||||
m.match();
|
||||
assertEquals(5, m.getMappingSet().size());
|
||||
GreedySubtreeMatcher.MIN_HEIGHT = 0;
|
||||
AbstractBottomUpMatcher.SIZE_THRESHOLD = 5;
|
||||
m = new CompositeMatchers.ClassicGumtree(
|
||||
trees.getFirst().getRoot(), trees.getSecond().getRoot(), new MappingStore());
|
||||
m.match();
|
||||
assertEquals(6, m.getMappingSet().size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.hash.RollingHashGenerator;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestHash {
|
||||
|
||||
ITree root;
|
||||
|
||||
@Before // FIXME Could it be before class ?
|
||||
public void init() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRollingJavaHash() {
|
||||
ITree root = TreeLoader.getDummySrc();
|
||||
new RollingHashGenerator.JavaRollingHashGenerator().hash(root);
|
||||
assertEquals(-1381305887, root.getChild(0).getChild(0).getHash()); // for c
|
||||
assertEquals(-1380321823, root.getChild(0).getChild(1).getHash()); // for d
|
||||
assertEquals(-1762812253, root.getChild(0).getHash()); // for b
|
||||
assertEquals(-1407966943, root.getChild(1).getHash()); // for e
|
||||
assertEquals(-295599963, root.getHash()); // for a
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.test;
|
||||
|
||||
import com.github.gumtreediff.io.TreeIoUtils;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class TestMetadata {
|
||||
ITree someNode;
|
||||
TreeContext tc;
|
||||
|
||||
final String key = "key";
|
||||
final String v1 = "test";
|
||||
final String v2 = "other";
|
||||
final String v3 = "more";
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
tc = new TreeContext();
|
||||
someNode = tc.createTree(0, "", "");
|
||||
tc.setRoot(someNode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutNode() throws Exception {
|
||||
assertNull(someNode.getMetadata(key));
|
||||
assertNull(someNode.setMetadata(key, v1));
|
||||
assertEquals(v1, someNode.getMetadata(key));
|
||||
assertEquals(v1, someNode.setMetadata(key, v2));
|
||||
assertEquals(v2, someNode.getMetadata(key));
|
||||
assertEquals(v2, someNode.setMetadata(key, null));
|
||||
assertNull(someNode.setMetadata(key, v2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGlobalPutNode() throws Exception {
|
||||
assertNull(someNode.getMetadata(key));
|
||||
assertNull(tc.setMetadata(key, v1));
|
||||
assertEquals(v1, tc.getMetadata(key));
|
||||
assertEquals(v1, tc.getMetadata(someNode, key));
|
||||
|
||||
assertEquals(v1, tc.setMetadata(key, v2));
|
||||
assertEquals(v2, tc.getMetadata(key));
|
||||
assertEquals(v2, tc.getMetadata(null, key));
|
||||
assertEquals(v2, tc.setMetadata(someNode, key, v1));
|
||||
assertEquals(v1, tc.getMetadata(someNode, key));
|
||||
assertEquals(v2, tc.getMetadata(key));
|
||||
assertEquals(v1, tc.setMetadata(someNode, key, null));
|
||||
assertEquals(v2, tc.getMetadata(someNode, key));
|
||||
assertEquals(v2, tc.setMetadata(someNode, key, v1));
|
||||
assertEquals(v1, someNode.setMetadata(key, null));
|
||||
assertEquals(v2, tc.getMetadata(someNode, key));
|
||||
assertEquals(v2, tc.setMetadata(null, key, v1));
|
||||
assertEquals(v1, tc.setMetadata(someNode, key, v2));
|
||||
assertEquals(v2, tc.getMetadata(someNode, key));
|
||||
assertEquals(v1, tc.getMetadata(null, key));
|
||||
assertEquals(v1, tc.getMetadata(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalIterator() throws Exception {
|
||||
String[] keys = {key, v1, v2, v3};
|
||||
Integer[] values = {0, 1, 2, 3};
|
||||
populate(keys, values, keys.length);
|
||||
|
||||
checkIterator(keys, values, someNode.getMetadata());
|
||||
}
|
||||
|
||||
private void checkIterator(String[] keys, Integer[] values, Iterator<Entry<String, Object>> it) {
|
||||
List<String> keyList = Arrays.asList(keys);
|
||||
List<String> seen = new ArrayList<>(keys.length);
|
||||
seen.addAll(keyList);
|
||||
while (it.hasNext()) {
|
||||
Entry<String, Object> e = it.next();
|
||||
int i = seen.indexOf(e.getKey());
|
||||
assertNotEquals("Iterate more than once", -1, i);
|
||||
seen.remove(i);
|
||||
i = keyList.indexOf(e.getKey());
|
||||
assertEquals("Not the right entry", i, (Object) values[i]);
|
||||
}
|
||||
assertEquals("Some metadata are not iterated", 0, seen.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGlobalIterator() throws Exception {
|
||||
final String v4 = "lastkey";
|
||||
String[] keys = {key, v1, v2, v3, v4};
|
||||
Integer[] values = {0, 1, 2, 3, 4};
|
||||
populate(keys, values, keys.length - 1);
|
||||
|
||||
tc.setMetadata(key, 5);
|
||||
tc.setMetadata(v4, 4);
|
||||
checkIterator(keys, values, tc.getMetadata(someNode));
|
||||
}
|
||||
|
||||
private void populate(String[] keys, Integer[] values, int size) {
|
||||
for (int i = 0; i < size; i ++)
|
||||
someNode.setMetadata(keys[i], values[i]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportCustom() throws Exception {
|
||||
final String pos = "pos";
|
||||
someNode.setMetadata(key, v1);
|
||||
someNode.setMetadata(pos, new int[]{1,2,3,4});
|
||||
tc.setMetadata(v2, v3);
|
||||
tc.setMetadata(v3, v3);
|
||||
|
||||
tc.export(key, v2);
|
||||
tc.export(pos, x -> Arrays.toString((int[]) x));
|
||||
|
||||
assertEquals("Export JSON", valJson, TreeIoUtils.toJson(tc).export(v3).toString());
|
||||
assertEquals("Export LISP", valLisp, TreeIoUtils.toLisp(tc).toString());
|
||||
assertEquals("Export XML", valXml, TreeIoUtils.toXml(tc).toString());
|
||||
assertEquals("Export Compact XML", valXmlCompact, TreeIoUtils.toCompactXml(tc).toString());
|
||||
|
||||
assertEquals(Sets.newHashSet(key, v2, pos), tc.getSerializers().exports());
|
||||
tc.getSerializers().remove(pos);
|
||||
assertEquals(Sets.newHashSet(key, v2), tc.getSerializers().exports());
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException.class)
|
||||
public void testExportInvalid1() {
|
||||
tc.export("Test key");
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException.class)
|
||||
public void testExportInvalid2() {
|
||||
TreeIoUtils.toJson(tc).export("Test key");
|
||||
}
|
||||
|
||||
final String valJson = "{\n"
|
||||
+ "\t\"other\": \"more\",\n"
|
||||
+ "\t\"more\": \"more\",\n"
|
||||
+ "\t\"root\": {\n"
|
||||
+ "\t\t\"type\": \"0\",\n"
|
||||
+ "\t\t\"key\": \"test\",\n"
|
||||
+ "\t\t\"pos\": \"[1, 2, 3, 4]\",\n"
|
||||
+ "\t\t\"children\": []\n"
|
||||
+ "\t}\n"
|
||||
+ "}";
|
||||
|
||||
final String valLisp = "(((:other \"more\") ) (0 \"0\" \"\" ((:key \"test\") (:pos \"[1, 2, 3, 4]\") ) ())";
|
||||
|
||||
final String valXml = "<?xml version=\"1.0\" ?>\n"
|
||||
+ "<root>\n"
|
||||
+ " <context>\n"
|
||||
+ " <other>more</other>\n"
|
||||
+ " </context>\n"
|
||||
+ " <tree type=\"0\">\n"
|
||||
+ " <key>test</key>\n"
|
||||
+ " <pos>[1, 2, 3, 4]</pos>\n"
|
||||
+ " </tree>\n"
|
||||
+ "</root>\n";
|
||||
|
||||
final String valXmlCompact = "<?xml version=\"1.0\" ?>\n"
|
||||
+ "<root other=\"more\">\n"
|
||||
+ " <0 key=\"test\" pos=\"[1, 2, 3, 4]\"/>\n"
|
||||
+ "</root>\n";
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2016 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.test;
|
||||
|
||||
|
||||
import com.github.gumtreediff.utils.Pair;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TestPair {
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
Pair<String, String> p1 = new Pair<>(new String("a"), new String("b"));
|
||||
Pair<String, String> p2 = new Pair<>(new String("a"), new String("b"));
|
||||
Pair<String, String> p3 = new Pair<>(new String("b"), new String("a"));
|
||||
assertTrue(p1.equals(p2));
|
||||
assertTrue(!p1.equals(p3));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.test;
|
||||
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.matchers.optimal.rted.RtedMatcher;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.utils.Pair;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TestRtedMatcher {
|
||||
|
||||
@Test
|
||||
public void testRtedMatcher() {
|
||||
Pair<TreeContext, TreeContext> trees = TreeLoader.getZsSlidePair();
|
||||
ITree src = trees.getFirst().getRoot();
|
||||
ITree dst = trees.getSecond().getRoot();
|
||||
Matcher matcher = new RtedMatcher(src, dst, new MappingStore());
|
||||
matcher.match();
|
||||
assertEquals(5, matcher.getMappingSet().size());
|
||||
assertTrue(matcher.getMappings().has(src, dst));
|
||||
assertTrue(matcher.getMappings().has(src.getChild(0).getChild(0), dst.getChild(0)));
|
||||
assertTrue(matcher.getMappings().has(src.getChild(0).getChild(0).getChild(0), dst.getChild(0).getChild(0)));
|
||||
assertTrue(matcher.getMappings().has(src.getChild(0).getChild(1), dst.getChild(1).getChild(0)));
|
||||
assertTrue(matcher.getMappings().has(src.getChild(0).getChild(2), dst.getChild(2)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeUtils;
|
||||
|
||||
public class TestTree {
|
||||
|
||||
@Test
|
||||
public void testIdComparator() {
|
||||
ITree root = TreeLoader.getDummySrc();
|
||||
List<ITree> nodes = root.getTrees();
|
||||
assertTrue(nodes.get(0).getLabel().equals("a"));
|
||||
assertTrue(nodes.get(1).getLabel().equals("b"));
|
||||
assertTrue(nodes.get(2).getLabel().equals("c"));
|
||||
assertTrue(nodes.get(3).getLabel().equals("d"));
|
||||
assertTrue(nodes.get(4).getLabel().equals("e"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetParents() {
|
||||
ITree tree = TreeLoader.getDummySrc();
|
||||
List<ITree> trees = new LinkedList<>(tree.getTrees());
|
||||
ITree n = trees.get(2);
|
||||
assertTrue(n.getLabel().equals("c"));
|
||||
List<ITree> parents = n.getParents();
|
||||
assertTrue(parents.size() == 2);
|
||||
assertTrue(parents.get(0).getLabel().equals("b"));
|
||||
assertTrue(parents.get(1).getLabel().equals("a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeepCopy() {
|
||||
ITree root = TreeLoader.getDummySrc();
|
||||
TreeUtils.postOrderNumbering(root);
|
||||
ITree croot = root.deepCopy();
|
||||
assertTrue(croot.getSize() == root.getSize());
|
||||
root.setLabel("new");
|
||||
root.getChildren().get(0).setLabel("new");
|
||||
root.getChildren().get(0).getChildren().get(0).setLabel("new");
|
||||
assertTrue(croot.getLabel().equals("a"));
|
||||
assertTrue(croot.getChildren().get(0).getLabel().equals("b"));
|
||||
assertTrue(croot.getChildren().get(0).getChildren().get(0).getLabel().equals("c"));
|
||||
assertTrue(root.getLabel().equals("new"));
|
||||
assertTrue(root.getChildren().get(0).getLabel().equals("new"));
|
||||
assertTrue(root.getChildren().get(0).getChildren().get(0).getLabel().equals("new"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsClone() {
|
||||
ITree tree = TreeLoader.getDummySrc();
|
||||
ITree copy = tree.deepCopy();
|
||||
assertTrue(tree.isIsomorphicTo(copy));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.test;
|
||||
|
||||
import com.github.gumtreediff.io.TreeIoUtils;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class TestTreeIoUtils {
|
||||
|
||||
@Test
|
||||
public void testSerializeTree() throws Exception {
|
||||
TreeContext tc = new TreeContext();
|
||||
ITree a = tc.createTree(0, "a", "type0");
|
||||
tc.setRoot(a);
|
||||
|
||||
ITree b = tc.createTree(1, "b", null);
|
||||
b.setParentAndUpdateChildren(a);
|
||||
ITree c = tc.createTree(3, "c", null);
|
||||
c.setParentAndUpdateChildren(b);
|
||||
ITree d = tc.createTree(3, "d", null);
|
||||
d.setParentAndUpdateChildren(b);
|
||||
ITree e = tc.createTree(2, null, null);
|
||||
e.setParentAndUpdateChildren(a);
|
||||
// Refresh metrics is called because it is automatically called in fromXML
|
||||
tc.validate();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
||||
TreeIoUtils.toXml(tc).writeTo(bos);
|
||||
TreeContext tca = TreeIoUtils.fromXml().generateFromString(bos.toString());
|
||||
ITree ca = tca.getRoot();
|
||||
|
||||
assertTrue(a.isIsomorphicTo(ca));
|
||||
assertTrue(ca.getType() == 0);
|
||||
assertTrue(tc.getTypeLabel(ca).equals("type0"));
|
||||
assertTrue(ca.getLabel().equals("a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadBigTree() {
|
||||
ITree big = TreeLoader.getDummyBig();
|
||||
assertEquals("a", big.getLabel());
|
||||
compareList(big.getChildren(), "b", "e", "f");
|
||||
}
|
||||
|
||||
void compareList(List<ITree> lst, String... expected) {
|
||||
ListIterator<ITree> it = lst.listIterator();
|
||||
for (String e: expected) {
|
||||
ITree n = it.next();
|
||||
assertEquals(e, n.getLabel());
|
||||
}
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.tree.TreeUtils;
|
||||
|
||||
public class TestTreeUtils {
|
||||
|
||||
@Test
|
||||
public void testPostOrderNumbering() {
|
||||
ITree root = TreeLoader.getDummySrc();
|
||||
TreeUtils.postOrderNumbering(root);
|
||||
assertEquals(4, root.getId());
|
||||
assertEquals(2, root.getChildren().get(0).getId());
|
||||
assertEquals(0, root.getChildren().get(0).getChildren().get(0).getId());
|
||||
assertEquals(1, root.getChildren().get(0).getChildren().get(1).getId());
|
||||
assertEquals(3, root.getChildren().get(1).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDepth() {
|
||||
ITree root = TreeLoader.getDummySrc();
|
||||
TreeUtils.computeDepth(root);
|
||||
assertEquals(0, root.getDepth());
|
||||
assertEquals(1, root.getChildren().get(0).getDepth());
|
||||
assertEquals(2, root.getChildren().get(0).getChildren().get(0).getDepth());
|
||||
assertEquals(2, root.getChildren().get(0).getChildren().get(1).getDepth());
|
||||
assertEquals(1, root.getChildren().get(1).getDepth());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeight() {
|
||||
ITree root = TreeLoader.getDummySrc();
|
||||
assertEquals(2, root.getHeight()); // depth of a
|
||||
assertEquals(1, root.getChildren().get(0).getHeight()); // depth of b
|
||||
assertEquals(0, root.getChildren().get(0).getChildren().get(0).getHeight()); // depth of c
|
||||
assertEquals(0, root.getChildren().get(0).getChildren().get(1).getHeight()); // depth of d
|
||||
assertEquals(0, root.getChildren().get(1).getHeight()); // depth of e
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreOrderNumbering() {
|
||||
ITree root = TreeLoader.getDummySrc();
|
||||
TreeUtils.preOrderNumbering(root);
|
||||
assertEquals(0, root.getId()); // id of a
|
||||
assertEquals(1, root.getChildren().get(0).getId()); // id of b
|
||||
assertEquals(2, root.getChildren().get(0).getChildren().get(0).getId()); // id of c
|
||||
assertEquals(3, root.getChildren().get(0).getChildren().get(1).getId()); // id of d
|
||||
assertEquals(4, root.getChildren().get(1).getId()); // id of e
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBreadthFirstNumbering() {
|
||||
ITree root = TreeLoader.getDummySrc();
|
||||
TreeUtils.breadthFirstNumbering(root);
|
||||
assertEquals(0, root.getId());
|
||||
assertEquals(1, root.getChildren().get(0).getId());
|
||||
assertEquals(2, root.getChildren().get(1).getId());
|
||||
assertEquals(3, root.getChildren().get(0).getChildren().get(0).getId());
|
||||
assertEquals(4, root.getChildren().get(0).getChildren().get(1).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostOrder() {
|
||||
ITree src = TreeLoader.getDummySrc();
|
||||
List<ITree> lst = TreeUtils.postOrder(src);
|
||||
Iterator<ITree> it = TreeUtils.postOrderIterator(src);
|
||||
compareListIterator(lst, it);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostOrder2() {
|
||||
ITree dst = TreeLoader.getDummyDst();
|
||||
List<ITree> lst = TreeUtils.postOrder(dst);
|
||||
Iterator<ITree> it = TreeUtils.postOrderIterator(dst);
|
||||
compareListIterator(lst, it);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostOrder3() {
|
||||
ITree big = TreeLoader.getDummyBig();
|
||||
List<ITree> lst = TreeUtils.postOrder(big);
|
||||
Iterator<ITree> it = TreeUtils.postOrderIterator(big);
|
||||
compareListIterator(lst, it);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBfs() {
|
||||
ITree src = TreeLoader.getDummySrc();
|
||||
List<ITree> lst = TreeUtils.breadthFirst(src);
|
||||
Iterator<ITree> it = TreeUtils.breadthFirstIterator(src);
|
||||
compareListIterator(lst, it);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBfsList() {
|
||||
ITree src = TreeLoader.getDummySrc();
|
||||
ITree dst = TreeLoader.getDummyDst();
|
||||
ITree big = TreeLoader.getDummyBig();
|
||||
compareListIterator(TreeUtils.breadthFirstIterator(src), "a", "b", "e", "c", "d");
|
||||
compareListIterator(TreeUtils.breadthFirstIterator(dst), "a", "f", "i", "b", "j", "c", "d", "h");
|
||||
compareListIterator(TreeUtils.breadthFirstIterator(big), "a", "b", "e", "f", "c",
|
||||
"d", "g", "l", "h", "m", "i", "j", "k");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreOrderList() {
|
||||
ITree src = TreeLoader.getDummySrc();
|
||||
ITree dst = TreeLoader.getDummyDst();
|
||||
ITree big = TreeLoader.getDummyBig();
|
||||
compareListIterator(TreeUtils.preOrderIterator(src), "a", "b", "c", "d", "e");
|
||||
compareListIterator(TreeUtils.preOrderIterator(dst), "a", "f", "b", "c", "d", "h", "i", "j");
|
||||
compareListIterator(TreeUtils.preOrderIterator(big), "a", "b", "c", "d", "e",
|
||||
"f", "g", "h", "i", "j", "k", "l", "m");
|
||||
}
|
||||
|
||||
void compareListIterator(List<ITree> lst, Iterator<ITree> it) {
|
||||
for (ITree i: lst) {
|
||||
assertEquals(i, it.next());
|
||||
}
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
void compareListIterator(Iterator<ITree> it, String... expected) {
|
||||
for (String e: expected) {
|
||||
ITree n = it.next();
|
||||
assertEquals(e, n.getLabel());
|
||||
}
|
||||
assertFalse("Iterator has next", it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBfs2() {
|
||||
ITree dst = TreeLoader.getDummyDst();
|
||||
List<ITree> lst = TreeUtils.breadthFirst(dst);
|
||||
Iterator<ITree> it = TreeUtils.breadthFirstIterator(dst);
|
||||
compareListIterator(lst, it);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBfs3() {
|
||||
ITree big = TreeLoader.getDummySrc();
|
||||
List<ITree> lst = TreeUtils.breadthFirst(big);
|
||||
Iterator<ITree> it = TreeUtils.breadthFirstIterator(big);
|
||||
compareListIterator(lst, it);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.test;
|
||||
|
||||
import com.github.gumtreediff.matchers.MappingStore;
|
||||
import com.github.gumtreediff.matchers.Matcher;
|
||||
import com.github.gumtreediff.matchers.optimal.zs.ZsMatcher;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.utils.Pair;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TestZsMatcher {
|
||||
|
||||
@Test
|
||||
public void testWithCustomExample() {
|
||||
Pair<TreeContext, TreeContext> trees = TreeLoader.getZsCustomPair();
|
||||
ITree src = trees.getFirst().getRoot();
|
||||
ITree dst = trees.getSecond().getRoot();
|
||||
Matcher matcher = new ZsMatcher(src, dst, new MappingStore());
|
||||
matcher.match();
|
||||
assertEquals(5, matcher.getMappingSet().size());
|
||||
assertTrue(matcher.getMappings().has(src, dst.getChild(0)));
|
||||
assertTrue(matcher.getMappings().has(src.getChild(0), dst.getChild(0).getChild(0)));
|
||||
assertTrue(matcher.getMappings().has(src.getChild(1), dst.getChild(0).getChild(1)));
|
||||
assertTrue(matcher.getMappings().has(src.getChild(1).getChild(0), dst.getChild(0).getChild(1).getChild(0)));
|
||||
assertTrue(matcher.getMappings().has(src.getChild(1).getChild(2), dst.getChild(0).getChild(1).getChild(2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithSlideExample() {
|
||||
Pair<TreeContext, TreeContext> trees = TreeLoader.getZsSlidePair();
|
||||
ITree src = trees.getFirst().getRoot();
|
||||
ITree dst = trees.getSecond().getRoot();
|
||||
Matcher matcher = new ZsMatcher(src, dst, new MappingStore());
|
||||
matcher.match();
|
||||
assertEquals(5, matcher.getMappingSet().size());
|
||||
assertTrue(matcher.getMappings().has(src, dst));
|
||||
assertTrue(matcher.getMappings().has(src.getChild(0).getChild(0), dst.getChild(0)));
|
||||
assertTrue(matcher.getMappings().has(src.getChild(0).getChild(0).getChild(0), dst.getChild(0).getChild(0)));
|
||||
assertTrue(matcher.getMappings().has(src.getChild(0).getChild(1), dst.getChild(1).getChild(0)));
|
||||
assertTrue(matcher.getMappings().has(src.getChild(0).getChild(2), dst.getChild(2)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* This file is part of GumTree.
|
||||
*
|
||||
* GumTree 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
* Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
*/
|
||||
|
||||
package com.github.gumtreediff.test;
|
||||
|
||||
import com.github.gumtreediff.io.TreeIoUtils;
|
||||
import com.github.gumtreediff.tree.ITree;
|
||||
import com.github.gumtreediff.utils.Pair;
|
||||
import com.github.gumtreediff.tree.TreeContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class TreeLoader {
|
||||
|
||||
private TreeLoader() {}
|
||||
|
||||
public static Pair<TreeContext, TreeContext> getActionPair() {
|
||||
return new Pair<>(load("/action_v0.xml"), load("/action_v1.xml"));
|
||||
}
|
||||
|
||||
public static Pair<TreeContext, TreeContext> getGumtreePair() {
|
||||
return new Pair<>(load("/gumtree_v0.xml"), load("/gumtree_v1.xml"));
|
||||
}
|
||||
|
||||
public static Pair<TreeContext, TreeContext> getZsCustomPair() {
|
||||
return new Pair<>(load("/zs_v0.xml"), load("/zs_v1.xml"));
|
||||
}
|
||||
|
||||
public static Pair<TreeContext, TreeContext> getZsSlidePair() {
|
||||
return new Pair<>(load("/zs_slide_v0.xml"), load("/zs_slide_v1.xml"));
|
||||
}
|
||||
|
||||
public static Pair<TreeContext, TreeContext> getDummyPair() {
|
||||
return new Pair<>(load("/Dummy_v0.xml"), load("/Dummy_v1.xml"));
|
||||
}
|
||||
|
||||
public static ITree getDummySrc() {
|
||||
return load("/Dummy_v0.xml").getRoot();
|
||||
}
|
||||
|
||||
public static ITree getDummyDst() {
|
||||
return load("/Dummy_v1.xml").getRoot();
|
||||
}
|
||||
|
||||
public static ITree getDummyBig() {
|
||||
return load("/Dummy_big.xml").getRoot();
|
||||
}
|
||||
|
||||
public static TreeContext load(String name) {
|
||||
try {
|
||||
return TreeIoUtils.fromXml().generateFromStream(System.class.getResourceAsStream(name));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(String.format("Unable to load test ressorce: %s", name), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<!--
|
||||
This file is part of GumTree.
|
||||
|
||||
GumTree 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
-->
|
||||
<tree type="0" label="a" >
|
||||
<tree type="1" label="b" >
|
||||
<tree type="2" label="c" />
|
||||
<tree type="2" label="d" />
|
||||
</tree>
|
||||
<tree type="1" label="e" />
|
||||
<tree type="1" label="f" >
|
||||
<tree type="2" label="g" >
|
||||
<tree type="3" label="h" >
|
||||
<tree type="4" label="i" />
|
||||
<tree type="4" label="j" />
|
||||
<tree type="4" label="k" />
|
||||
</tree>
|
||||
</tree>
|
||||
<tree type="2" label="l" >
|
||||
<tree type="3" label="m" />
|
||||
</tree>
|
||||
</tree>
|
||||
</tree>
|
||||
@@ -0,0 +1,26 @@
|
||||
<!--
|
||||
This file is part of GumTree.
|
||||
|
||||
GumTree 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
-->
|
||||
<tree type="0" typeLabel="0" label="a" pos="0" length="0">
|
||||
<tree type="1" typeLabel="1" label="b" pos="0" length="0">
|
||||
<tree type="3" typeLabel="3" label="c" pos="0" length="0"/>
|
||||
<tree type="3" typeLabel="3" label="d" pos="0" length="0"/>
|
||||
</tree>
|
||||
<tree type="2" typeLabel="2" label="e" pos="0" length="0"/>
|
||||
</tree>
|
||||
@@ -0,0 +1,31 @@
|
||||
<!--
|
||||
This file is part of GumTree.
|
||||
|
||||
GumTree 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
-->
|
||||
<tree type="0" typeLabel="0" label="a" pos="0" length="0">
|
||||
<tree type="4" typeLabel="4" label="f" pos="0" length="0">
|
||||
<tree type="1" typeLabel="1" label="b" pos="0" length="0">
|
||||
<tree type="3" typeLabel="3" label="c" pos="0" length="0"/>
|
||||
<tree type="3" typeLabel="3" label="d" pos="0" length="0"/>
|
||||
<tree type="5" typeLabel="5" label="h" pos="0" length="0"/>
|
||||
</tree>
|
||||
</tree>
|
||||
<tree type="2" typeLabel="2" label="i" pos="0" length="0">
|
||||
<tree type="2" typeLabel="2" label="j" pos="0" length="0"/>
|
||||
</tree>
|
||||
</tree>
|
||||
@@ -0,0 +1,29 @@
|
||||
<!--
|
||||
This file is part of GumTree.
|
||||
|
||||
GumTree 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
-->
|
||||
<tree type="0" label="a">
|
||||
<tree type="0" label="e">
|
||||
<tree type="0" label="f" />
|
||||
</tree>
|
||||
<tree type="0" label="b">
|
||||
<tree type="0" label="c" />
|
||||
<tree type="0" label="d" />
|
||||
</tree>
|
||||
<tree type="0" label="g" />
|
||||
</tree>
|
||||
@@ -0,0 +1,30 @@
|
||||
<!--
|
||||
This file is part of GumTree.
|
||||
|
||||
GumTree 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
-->
|
||||
<tree type="0" label="z">
|
||||
<tree type="0" label="b">
|
||||
<tree type="0" label="c" />
|
||||
<tree type="0" label="d" />
|
||||
</tree>
|
||||
<tree type="1" label="h">
|
||||
<tree type="0" label="e">
|
||||
<tree type="0" label="y" />
|
||||
</tree>
|
||||
</tree>
|
||||
</tree>
|
||||
@@ -0,0 +1,29 @@
|
||||
<!--
|
||||
This file is part of GumTree.
|
||||
|
||||
GumTree 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
-->
|
||||
<tree type="0" label="a">
|
||||
<tree type="0" label="e">
|
||||
<tree type="0" label="f" />
|
||||
</tree>
|
||||
<tree type="0" label="b">
|
||||
<tree type="0" label="c" />
|
||||
<tree type="0" label="d" />
|
||||
</tree>
|
||||
<tree type="0" label="g" />
|
||||
</tree>
|
||||
@@ -0,0 +1,31 @@
|
||||
<!--
|
||||
This file is part of GumTree.
|
||||
|
||||
GumTree 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
-->
|
||||
<tree type="0" label="z">
|
||||
<tree type="0" label="b">
|
||||
<tree type="0" label="c" />
|
||||
<tree type="0" label="d" />
|
||||
</tree>
|
||||
<tree type="1" label="h">
|
||||
<tree type="0" label="e">
|
||||
<tree type="0" label="y" />
|
||||
</tree>
|
||||
</tree>
|
||||
<tree type="0" label="g" />
|
||||
</tree>
|
||||
@@ -0,0 +1,28 @@
|
||||
<!--
|
||||
This file is part of GumTree.
|
||||
|
||||
GumTree 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
-->
|
||||
<tree type="0" label="6">
|
||||
<tree type="0" label="5">
|
||||
<tree type="0" label="2">
|
||||
<tree type="0" label="1" />
|
||||
</tree>
|
||||
<tree type="0" label="3" />
|
||||
<tree type="0" label="4" />
|
||||
</tree>
|
||||
</tree>
|
||||
@@ -0,0 +1,28 @@
|
||||
<!--
|
||||
This file is part of GumTree.
|
||||
|
||||
GumTree 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
-->
|
||||
<tree type="0" label="6">
|
||||
<tree type="0" label="2">
|
||||
<tree type="0" label="1" />
|
||||
</tree>
|
||||
<tree type="0" label="4">
|
||||
<tree type="0" label="3" />
|
||||
</tree>
|
||||
<tree type="0" label="5" />
|
||||
</tree>
|
||||
@@ -0,0 +1,27 @@
|
||||
<!--
|
||||
This file is part of GumTree.
|
||||
|
||||
GumTree 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
-->
|
||||
<tree type="0" label="a">
|
||||
<tree type="0" label="b"/>
|
||||
<tree type="0" label="c">
|
||||
<tree type="0" label="d"/>
|
||||
<tree type="0" label="e"/>
|
||||
<tree type="0" label="f"/>
|
||||
</tree>
|
||||
</tree>
|
||||
@@ -0,0 +1,29 @@
|
||||
<!--
|
||||
This file is part of GumTree.
|
||||
|
||||
GumTree 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GumTree 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 GumTree. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com>
|
||||
Copyright 2011-2015 Floréal Morandat <florealm@gmail.com>
|
||||
-->
|
||||
<tree type="0" label="z">
|
||||
<tree type="0" label="a">
|
||||
<tree type="0" label="b" />
|
||||
<tree type="0" label="c">
|
||||
<tree type="0" label="y" />
|
||||
<tree type="1" label="e" />
|
||||
<tree type="0" label="f" />
|
||||
</tree>
|
||||
</tree>
|
||||
</tree>
|
||||
Reference in New Issue
Block a user