repo-reorg-2020: moved sts4-relevant spring-ide bundles from https://github.com/spring-projects/spring-ide/releases/tag/REPO-REORG-2020 into sts4 repo
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.springframework.ide.eclipse.maven.pom</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -0,0 +1,7 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
||||
@@ -0,0 +1,19 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Maven POM Compare
|
||||
Bundle-SymbolicName: org.springframework.ide.eclipse.maven.pom;singleton:=true
|
||||
Bundle-Version: 4.8.1.qualifier
|
||||
Automatic-Module-Name: org.springframework.ide.eclipse.maven.pom
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Require-Bundle: org.eclipse.compare,
|
||||
org.eclipse.core.runtime,
|
||||
org.eclipse.ui.workbench,
|
||||
org.eclipse.jface,
|
||||
com.google.guava,
|
||||
org.eclipse.jface.text,
|
||||
org.springsource.ide.eclipse.commons.livexp,
|
||||
org.springsource.ide.eclipse.commons.frameworks.core
|
||||
Bundle-Activator: org.springframework.ide.eclipse.maven.pom.PomPlugin
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Export-Package: org.springframework.ide.eclipse.maven.pom
|
||||
Bundle-Vendor: Pivotal Inc.
|
||||
@@ -0,0 +1,5 @@
|
||||
source.. = src/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.,\
|
||||
plugin.xml
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?eclipse version="3.4"?>
|
||||
<plugin>
|
||||
<extension
|
||||
point="org.eclipse.compare.contentMergeViewers">
|
||||
<viewer
|
||||
class="org.springframework.ide.eclipse.maven.pom.PomMergeViewerCreator"
|
||||
id="org.springframework.ide.eclipse.maven.pom"
|
||||
label="Maven POM Compare">
|
||||
</viewer>
|
||||
<contentTypeBinding
|
||||
contentMergeViewerId="org.springframework.ide.eclipse.maven.pom"
|
||||
contentTypeId="org.eclipse.m2e.core.pomFile">
|
||||
</contentTypeBinding>
|
||||
</extension>
|
||||
|
||||
</plugin>
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot.ide</groupId>
|
||||
<artifactId>org.springframework.boot.ide</artifactId>
|
||||
<version>4.8.1-SNAPSHOT</version>
|
||||
<relativePath>../../eclipse-distribution/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>org.springframework.ide.eclipse</groupId>
|
||||
<artifactId>org.springframework.ide.eclipse.maven.pom</artifactId>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.eclipse.tycho</groupId>
|
||||
<artifactId>target-platform-configuration</artifactId>
|
||||
<version>${tycho-version}</version>
|
||||
<configuration>
|
||||
<resolver>p2</resolver>
|
||||
<pomDependencies>ignore</pomDependencies>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.eclipse.tycho</groupId>
|
||||
<artifactId>tycho-compiler-plugin</artifactId>
|
||||
<version>${tycho-version}</version>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.eclipse.tycho</groupId>
|
||||
<artifactId>tycho-source-plugin</artifactId>
|
||||
<version>${tycho-version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>plugin-source</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>plugin-source</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.eclipse.tycho</groupId>
|
||||
<artifactId>tycho-p2-plugin</artifactId>
|
||||
<version>${tycho-version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>second-generate-p2-metadata</id>
|
||||
<goals>
|
||||
<goal>p2-metadata</goal>
|
||||
</goals>
|
||||
<phase>verify</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,269 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.maven.pom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.compare.ITypedElement;
|
||||
import org.eclipse.compare.contentmergeviewer.IDocumentRange;
|
||||
import org.eclipse.compare.structuremergeviewer.IStructureComparator;
|
||||
import org.eclipse.jface.text.BadLocationException;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.Position;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
import org.springsource.ide.eclipse.commons.frameworks.core.util.DocumentRegion;
|
||||
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
|
||||
import org.xml.sax.Locator;
|
||||
|
||||
public class DomStructureComparable implements IStructureComparator, IDocumentRange, ITypedElement {
|
||||
|
||||
final private DomType type;
|
||||
private Position range;
|
||||
private IDocument document;
|
||||
private DomStructureComparable[] children;
|
||||
private Object id;
|
||||
private String name;
|
||||
private DomStructureComparable parent;
|
||||
|
||||
public enum DomType {
|
||||
ROOT,
|
||||
ELEMENT,
|
||||
ATTRIBUTE,
|
||||
TEXT
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
final public DomType type;
|
||||
int start;
|
||||
int end;
|
||||
final public IDocument document;
|
||||
Builder parent;
|
||||
|
||||
List<DomStructureComparable> children = new ArrayList<>();
|
||||
String name;
|
||||
|
||||
public Builder(DomType type, IDocument document) {
|
||||
this.type = type;
|
||||
this.document = document;
|
||||
}
|
||||
|
||||
public void start(Locator locator) {
|
||||
try {
|
||||
int offset = document.getLineOffset(locator.getLineNumber() - 1) + locator.getColumnNumber() - 1;
|
||||
start = adjustStartPositionForElement(document, offset);
|
||||
} catch (BadLocationException e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void end(Locator locator) {
|
||||
if (locator.getLineNumber() < 0) {
|
||||
end = document.getLength();
|
||||
} else {
|
||||
try {
|
||||
int offset = document.getLineOffset(locator.getLineNumber() - 1) + locator.getColumnNumber() - 1;
|
||||
end = adjustEndPositionForElement(document, offset);
|
||||
} catch (BadLocationException e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Builder parent(Builder parent) {
|
||||
this.parent = parent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void addChild(DomStructureComparable child) {
|
||||
this.children.add(child);
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DomStructureComparable build(Object id) {
|
||||
return new DomStructureComparable(
|
||||
id,
|
||||
type,
|
||||
name,
|
||||
document,
|
||||
new Position(start, end -start),
|
||||
children.toArray(new DomStructureComparable[children.size()]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static DomStructureComparable.Builder createRoot(IDocument doc) {
|
||||
return new Builder(DomType.ROOT, doc);
|
||||
}
|
||||
|
||||
public DomStructureComparable(Object id, DomType type, String name, IDocument document, Position range, DomStructureComparable[] children) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.document = document;
|
||||
this.range = range;
|
||||
this.children = children;
|
||||
for (DomStructureComparable child : children) {
|
||||
child.parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public DomType getDomType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDocument getDocument() {
|
||||
return document;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getRange() {
|
||||
return range;
|
||||
}
|
||||
|
||||
public static Builder createElement(IDocument document, Builder parent, String name) {
|
||||
return new Builder(DomType.ELEMENT, document).parent(parent).name(name);
|
||||
}
|
||||
|
||||
public static Builder createText(IDocument document, Builder parent) {
|
||||
return new Builder(DomType.TEXT, document).parent(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param doc
|
||||
* @param offset - opening tag '>' offset
|
||||
* @return
|
||||
* @throws BadLocationException
|
||||
*/
|
||||
private static int adjustStartPositionForElement(IDocument doc, int offset) throws BadLocationException {
|
||||
if (offset > 0) {
|
||||
DocumentRegion region = new DocumentRegion(doc, offset, offset);
|
||||
region = region.extendBeforeUntil('<');
|
||||
DocumentRegion before = region.textBeforeOnLine();
|
||||
if (before.isWhiteSpace()) {
|
||||
return before.getStart();
|
||||
} else {
|
||||
return region.getStart();
|
||||
}
|
||||
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static int adjustEndPositionForElement(IDocument doc, int offset) throws BadLocationException {
|
||||
DocumentRegion region = new DocumentRegion(doc, offset, offset);
|
||||
DocumentRegion after = region.textAfterOnLine();
|
||||
if (after.isWhiteSpace()) {
|
||||
DocumentRegion newLine = after.textAfter(2);
|
||||
char c1 = newLine.charAt(0);
|
||||
char c2 = newLine.charAt(1);
|
||||
if (c1 == '\n') {
|
||||
if (c2 == '\r') {
|
||||
return after.getEnd() + 2;
|
||||
} else {
|
||||
return after.getEnd() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return region.getEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image getImage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return this.type.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((id == null) ? 0 : id.hashCode());
|
||||
result = prime * result + ((type == null) ? 0 : type.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
DomStructureComparable other = (DomStructureComparable) obj;
|
||||
if (id == null) {
|
||||
if (other.id != null)
|
||||
return false;
|
||||
} else if (!id.equals(other.id))
|
||||
return false;
|
||||
if (type != other.type)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes opening and closing tags
|
||||
* @return
|
||||
*/
|
||||
public String getTextContent() {
|
||||
try {
|
||||
return document.get(range.offset, range.length);
|
||||
} catch (BadLocationException e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Text between the tags
|
||||
* @return
|
||||
*/
|
||||
public String getValue() {
|
||||
try {
|
||||
String s = getDocument().get(getRange().offset, getRange().length);
|
||||
int start = s.indexOf('>');
|
||||
int end = s.lastIndexOf('<');
|
||||
if (start >= 0 && start < end) {
|
||||
return s.substring(start + 1, end);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
public DomStructureComparable getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.maven.pom;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface IdProvider {
|
||||
|
||||
Object id(DomStructureComparable.Builder node);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.maven.pom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class IdProviderRegistry {
|
||||
|
||||
static class Registration {
|
||||
Predicate<DomStructureComparable.Builder> selector;
|
||||
IdProvider provider;
|
||||
public Registration(Predicate<DomStructureComparable.Builder> selector, IdProvider provider) {
|
||||
super();
|
||||
this.selector = selector;
|
||||
this.provider = provider;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + selector + " -> " + provider + "]\n";
|
||||
}
|
||||
}
|
||||
|
||||
private List<Registration> registry = new ArrayList<>();
|
||||
|
||||
public void add(Predicate<DomStructureComparable.Builder> selector, IdProvider provider) {
|
||||
registry.add(new Registration(selector, provider));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return registry.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param partialNode not fully initialized
|
||||
* @return
|
||||
*/
|
||||
public Object getId(DomStructureComparable.Builder partialNode) {
|
||||
for (Registration registration : registry) {
|
||||
if (registration.selector.test(partialNode)) {
|
||||
return registration.provider.id(partialNode);
|
||||
}
|
||||
};
|
||||
return IdProviders.DEFAULT.id(partialNode);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.maven.pom;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
|
||||
public class IdProviders {
|
||||
|
||||
static final IdProvider FROM_TAG_NAME = (node) -> node.name;
|
||||
|
||||
public static final IdProvider DEFAULT = (node) -> node.name + (node.parent == null ? "" : node.parent.children.size());
|
||||
|
||||
public static IdProvider fromChildren(String... tagNames) {
|
||||
ImmutableSet<String> tagSet = ImmutableSet.copyOf(tagNames);
|
||||
return (node) -> {
|
||||
Map<String, String> tagToValue = new HashMap<>();
|
||||
for (DomStructureComparable child : node.children) {
|
||||
if (tagSet.contains(child.getName())) {
|
||||
tagToValue.put(child.getName(), child.getValue());
|
||||
}
|
||||
}
|
||||
Builder<Object> builder = ImmutableList.builder();
|
||||
for (String tagName : tagNames) {
|
||||
String value = tagToValue.get(tagName);
|
||||
if (value != null) {
|
||||
builder.add(value);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
};
|
||||
}
|
||||
|
||||
// static final IdProvider DEPENDENCY = (node) -> {
|
||||
// XMLNode group = null;
|
||||
// XMLNode artifact = null;
|
||||
// Object[] children = node.getChildren();
|
||||
// for (int i = 0; i < children.length && (group == null || artifact == null); i++) {
|
||||
// if (children[i] instanceof XMLNode) {
|
||||
// XMLNode n = (XMLNode) children[i];
|
||||
// if ("groupId".equals(n.getElement())) {
|
||||
// group = n;
|
||||
// } else if ("artifactId".equals(n.getElement())) {
|
||||
// artifact = n;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
// sb.append(getTextContent(group));
|
||||
// sb.append(' ');
|
||||
// sb.append(getTextContent(artifact));
|
||||
// return sb.toString();
|
||||
// };
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.maven.pom;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.springframework.ide.eclipse.maven.pom.DomStructureComparable.DomType;
|
||||
import org.springframework.ide.eclipse.maven.pom.XmlDocumentDiffer.Difference;
|
||||
import org.springframework.ide.eclipse.maven.pom.XmlDocumentDiffer.Direction;
|
||||
|
||||
public class PomDocumentDiffer {
|
||||
|
||||
public static XmlDocumentDiffer create(IDocument document1, IDocument document2) {
|
||||
return new XmlDocumentDiffer(document1, document2)
|
||||
.idProvider(XmlSelectors.childrenOf("properties", "project", "parent", "dependency", "repository",
|
||||
"pluginRepository", "plugin", "build"), IdProviders.FROM_TAG_NAME)
|
||||
.idProvider(XmlSelectors.tagName("dependency"), IdProviders.fromChildren("groupId", "artifactId"))
|
||||
.idProvider(XmlSelectors.tagName("repository"), IdProviders.fromChildren("url"))
|
||||
.idProvider(XmlSelectors.tagName("pluginRepository"), IdProviders.fromChildren("url"))
|
||||
.idProvider(XmlSelectors.tagName("plugin"), IdProviders.fromChildren("groupId", "artifactId"))
|
||||
.idProvider(XmlSelectors.path("dependency", "exclusions", "exclusion"),
|
||||
IdProviders.fromChildren("groupId", "artifactId"))
|
||||
.idProvider(XmlSelectors.path("dependency", "exclusions", "exclusion", "*"),
|
||||
IdProviders.FROM_TAG_NAME);
|
||||
}
|
||||
|
||||
public static Predicate<Difference> differenceDirections(Direction... directions) {
|
||||
return new Predicate<Difference>() {
|
||||
|
||||
@Override
|
||||
public boolean test(Difference t) {
|
||||
return Arrays.asList(directions).contains(t.direction);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public static Predicate<Difference> ignorePath(String... path) {
|
||||
return new Predicate<Difference>() {
|
||||
|
||||
@Override
|
||||
public boolean test(Difference t) {
|
||||
if (t.leftComparable != null) {
|
||||
return !samePath(t.leftComparable);
|
||||
}
|
||||
if (t.rightComparable != null) {
|
||||
return !samePath(t.rightComparable);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean samePath(DomStructureComparable structure) {
|
||||
for (int i = path.length - 1; i >= 0; i--) {
|
||||
if (structure == null || structure.getDomType() == DomType.ROOT) {
|
||||
return false;
|
||||
} else {
|
||||
if (path[i].equals(structure.getName())) {
|
||||
structure = structure.getParent();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.maven.pom;
|
||||
|
||||
import static org.springframework.ide.eclipse.maven.pom.PomDocumentDiffer.differenceDirections;
|
||||
import static org.springframework.ide.eclipse.maven.pom.PomDocumentDiffer.ignorePath;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.compare.CompareConfiguration;
|
||||
import org.eclipse.compare.internal.MergeViewerContentProvider;
|
||||
import org.eclipse.compare.internal.merge.DocumentMerger;
|
||||
import org.eclipse.compare.rangedifferencer.RangeDifference;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.jface.text.BadLocationException;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.Position;
|
||||
import org.springframework.ide.eclipse.maven.pom.XmlDocumentDiffer.Difference;
|
||||
import org.springframework.ide.eclipse.maven.pom.XmlDocumentDiffer.Direction;
|
||||
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
|
||||
|
||||
@SuppressWarnings("restriction")
|
||||
public class PomDocumentMerger extends DocumentMerger {
|
||||
|
||||
private CompareConfiguration configuration;
|
||||
|
||||
public PomDocumentMerger(IDocumentMergerInput input, CompareConfiguration configuration) {
|
||||
super(input);
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
private ArrayList<Diff> calculateDiffs() {
|
||||
ArrayList<Diff> diffs = new ArrayList<>();
|
||||
IDocument lDoc = getDocument(MergeViewerContentProvider.LEFT_CONTRIBUTOR);
|
||||
IDocument rDoc = getDocument(MergeViewerContentProvider.RIGHT_CONTRIBUTOR);
|
||||
|
||||
if (lDoc == null || rDoc == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Position lRegion = getRegion(MergeViewerContentProvider.LEFT_CONTRIBUTOR);
|
||||
Position rRegion = getRegion(MergeViewerContentProvider.RIGHT_CONTRIBUTOR);
|
||||
|
||||
resetPositions(lDoc);
|
||||
resetPositions(rDoc);
|
||||
|
||||
boolean ignoreWhiteSpace = true;
|
||||
|
||||
|
||||
List<Difference> diffNodes = PomDocumentDiffer.create(lDoc, rDoc)
|
||||
.filter(differenceDirections(configuration.isMirrored() ? Direction.LEFT : Direction.RIGHT)
|
||||
.and(ignorePath("project", "name"))
|
||||
.and(ignorePath("project", "description"))
|
||||
.and(ignorePath("project", "groupId"))
|
||||
.and(ignorePath("project", "version"))
|
||||
.and(ignorePath("project", "parent", "relativePath"))
|
||||
)
|
||||
.getDiffs();
|
||||
|
||||
for (Difference d : diffNodes) {
|
||||
Diff difference = null;
|
||||
|
||||
switch (d.direction) {
|
||||
case BOTH:
|
||||
case NONE:
|
||||
case LEFT:
|
||||
case RIGHT:
|
||||
difference = newDiff(
|
||||
null, d.direction == Direction.NONE ? RangeDifference.NOCHANGE : RangeDifference.CHANGE, null, null, 0, 0,
|
||||
lDoc, lRegion, d.leftRange.offset, d.leftRange.offset + d.leftRange.length,
|
||||
rDoc, rRegion, d.rightRange.offset, d.rightRange.offset + d.rightRange.length
|
||||
);
|
||||
diffs.add(difference);
|
||||
break;
|
||||
}
|
||||
|
||||
if (difference != null) {
|
||||
// Extract the string for each contributor.
|
||||
String leftStr = "";
|
||||
String rightStr = "";
|
||||
try {
|
||||
leftStr= lDoc.get(d.leftRange.offset, d.leftRange.length);
|
||||
rightStr= rDoc.get(d.rightRange.offset, d.rightRange.length);
|
||||
} catch (BadLocationException e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
|
||||
// Indicate whether all contributors are whitespace
|
||||
if (ignoreWhiteSpace
|
||||
&& leftStr.trim().length() == 0
|
||||
&& rightStr.trim().length() == 0) {
|
||||
setDiffWhitespace(difference, true);
|
||||
}
|
||||
|
||||
// If the diff is of interest, record it and generate the token diffs
|
||||
if (useChange(difference)) {
|
||||
recordChangeDiff(difference);
|
||||
if (leftStr.length() > 0 && rightStr.length() > 0) {
|
||||
// if (USE_MERGING_TOKEN_DIFF)
|
||||
// mergingTokenDiff(diff, aDoc, a, rDoc, d, lDoc, s);
|
||||
// else
|
||||
simpleTokenDiff(difference, null, null, rDoc, rightStr, lDoc, leftStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return diffs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void doDiff() throws CoreException {
|
||||
setChangeDiffs(new ArrayList<>());
|
||||
setAllDiffs(calculateDiffs());
|
||||
}
|
||||
|
||||
private IDocument getDocument(char contributor) {
|
||||
try {
|
||||
Method method = DocumentMerger.class.getDeclaredMethod("getDocument", char.class);
|
||||
method.setAccessible(true);
|
||||
return (IDocument) method.invoke(this, contributor);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Position getRegion(char contributor) {
|
||||
try {
|
||||
Method method = DocumentMerger.class.getDeclaredMethod("getRegion", char.class);
|
||||
method.setAccessible(true);
|
||||
return (Position) method.invoke(this, contributor);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetPositions(IDocument doc) {
|
||||
try {
|
||||
Method method = DocumentMerger.class.getDeclaredMethod("resetPositions", IDocument.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(this, doc);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void recordChangeDiff(Diff diff) {
|
||||
try {
|
||||
Method method = DocumentMerger.class.getDeclaredMethod("recordChangeDiff", Diff.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(this, diff);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void simpleTokenDiff(final Diff baseDiff,
|
||||
IDocument ancestorDoc, String a,
|
||||
IDocument rightDoc, String d,
|
||||
IDocument leftDoc, String s) {
|
||||
try {
|
||||
Method method = DocumentMerger.class.getDeclaredMethod("simpleTokenDiff", Diff.class,
|
||||
IDocument.class, String.class,
|
||||
IDocument.class, String.class,
|
||||
IDocument.class, String.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(this, baseDiff, ancestorDoc, a, rightDoc, d, leftDoc, s);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setChangeDiffs(ArrayList<Diff> l) {
|
||||
try {
|
||||
Field field = DocumentMerger.class.getDeclaredField("fChangeDiffs");
|
||||
field.setAccessible(true);
|
||||
field.set(this, l);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setAllDiffs(ArrayList<Diff> l) {
|
||||
try {
|
||||
Field field = DocumentMerger.class.getDeclaredField("fAllDiffs");
|
||||
field.setAccessible(true);
|
||||
field.set(this, l);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Diff newDiff(Diff parent, int dir, IDocument ancestorDoc, Position aRange, int ancestorStart,
|
||||
int ancestorEnd,
|
||||
IDocument leftDoc, Position lRange, int leftStart, int leftEnd,
|
||||
IDocument rightDoc, Position rRange, int rightStart, int rightEnd) {
|
||||
|
||||
try {
|
||||
Constructor<Diff> constructor = Diff.class.getDeclaredConstructor(DocumentMerger.class, Diff.class, int.class,
|
||||
IDocument.class, Position.class, int.class, int.class,
|
||||
IDocument.class, Position.class, int.class, int.class,
|
||||
IDocument.class, Position.class, int.class, int.class);
|
||||
constructor.setAccessible(true);
|
||||
return (Diff) constructor.newInstance(this, parent, dir, ancestorDoc, aRange, ancestorStart, ancestorEnd,
|
||||
leftDoc, lRange, leftStart, leftEnd, rightDoc, rRange, rightStart, rightEnd);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setDiffWhitespace(Diff diff, boolean whitespace) {
|
||||
try {
|
||||
Field field = Diff.class.getDeclaredField("fIsWhitespace");
|
||||
field.setAccessible(true);
|
||||
field.set(diff, whitespace);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.maven.pom;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.eclipse.compare.CompareConfiguration;
|
||||
import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
|
||||
import org.eclipse.compare.internal.merge.DocumentMerger;
|
||||
import org.eclipse.compare.internal.merge.DocumentMerger.Diff;
|
||||
import org.eclipse.compare.internal.merge.DocumentMerger.IDocumentMergerInput;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.eclipse.jface.text.TextViewer;
|
||||
import org.eclipse.jface.text.source.IOverviewRuler;
|
||||
import org.eclipse.jface.text.source.IVerticalRuler;
|
||||
import org.eclipse.jface.text.source.SourceViewer;
|
||||
import org.eclipse.jface.text.source.SourceViewerConfiguration;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
|
||||
import org.springsource.ide.eclipse.commons.livexp.util.Log;
|
||||
|
||||
@SuppressWarnings("restriction")
|
||||
public class PomMergeViewer extends TextMergeViewer {
|
||||
|
||||
public PomMergeViewer(Composite parent, int style, CompareConfiguration configuration) {
|
||||
|
||||
super(parent, style, configuration);
|
||||
if (Boolean.TRUE.equals(configuration.getProperty(PomPlugin.POM_STRUCTURE_ADDITIONS_COMPARE_SETTING))) {
|
||||
DocumentMerger original = getDocumentMerger();
|
||||
PomDocumentMerger pomMerger = new PomDocumentMerger(getInputFromMerger(original), configuration);
|
||||
setDocumentMerger(pomMerger);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "Maven POM Compare";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureTextViewer(TextViewer textViewer) {
|
||||
if (textViewer instanceof SourceViewer) {
|
||||
Bundle bundle = Platform.getBundle("org.eclipse.m2e.editor.xml");
|
||||
if (bundle != null) {
|
||||
try {
|
||||
Class<?> clazz = bundle.loadClass("org.eclipse.m2e.editor.xml.PomStructuredTextViewConfiguration");
|
||||
SourceViewerConfiguration config = (SourceViewerConfiguration) clazz.newInstance();
|
||||
((SourceViewer)textViewer).configure(config);
|
||||
return;
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.warn(e);
|
||||
} catch (InstantiationException e) {
|
||||
Log.warn(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
super.configureTextViewer(textViewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SourceViewer createSourceViewer(Composite parent, int textOrientation) {
|
||||
Bundle bundle = Platform.getBundle("org.eclipse.m2e.editor.xml");
|
||||
if (bundle != null) {
|
||||
try {
|
||||
Class<?> clazz = bundle.loadClass("org.eclipse.wst.sse.ui.internal.StructuredTextViewer");
|
||||
Constructor<?> constructor = clazz.getDeclaredConstructor(Composite.class, IVerticalRuler.class, IOverviewRuler.class, boolean.class, int.class);
|
||||
return (SourceViewer) constructor.newInstance(parent, null, null, false, textOrientation | SWT.H_SCROLL | SWT.V_SCROLL);
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.warn(e);
|
||||
} catch (InstantiationException e) {
|
||||
Log.warn(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.warn(e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.warn(e);
|
||||
} catch (SecurityException e) {
|
||||
Log.warn(e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.warn(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.warn(e);
|
||||
}
|
||||
}
|
||||
return super.createSourceViewer(parent, textOrientation);
|
||||
}
|
||||
|
||||
private IDocumentMergerInput getInputFromMerger(DocumentMerger merger) {
|
||||
try {
|
||||
Field field = DocumentMerger.class.getDeclaredField("fInput");
|
||||
field.setAccessible(true);
|
||||
return (IDocumentMergerInput) field.get(merger);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
private DocumentMerger getDocumentMerger() {
|
||||
try {
|
||||
Field field = TextMergeViewer.class.getDeclaredField("fMerger");
|
||||
field.setAccessible(true);
|
||||
return (DocumentMerger) field.get(this);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setDocumentMerger(DocumentMerger docMerger) {
|
||||
try {
|
||||
Field field = TextMergeViewer.class.getDeclaredField("fMerger");
|
||||
field.setAccessible(true);
|
||||
field.set(this, docMerger);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void copy(boolean leftToRight) {
|
||||
if (Boolean.TRUE.equals(getCompareConfiguration().getProperty(PomPlugin.POM_STRUCTURE_ADDITIONS_COMPARE_SETTING))) {
|
||||
try {
|
||||
Method copyDiffMethod = TextMergeViewer.class.getDeclaredMethod("copy", Diff.class, boolean.class);
|
||||
copyDiffMethod.setAccessible(true);
|
||||
|
||||
for (Iterator<Diff> it = ((PomDocumentMerger)getDocumentMerger()).changesIterator(); it.hasNext();) {
|
||||
Diff d = it.next();
|
||||
copyDiffMethod.invoke(this, d, leftToRight);
|
||||
}
|
||||
|
||||
// Execute: update(false);
|
||||
Method updateMethod = TextMergeViewer.class.getDeclaredMethod("update", boolean.class);
|
||||
updateMethod.setAccessible(true);
|
||||
updateMethod.invoke(this, false);
|
||||
|
||||
// Execute: selectFirstDiff(true)
|
||||
Method selectFirstDiffMethod = TextMergeViewer.class.getDeclaredMethod("selectFirstDiff", boolean.class);
|
||||
selectFirstDiffMethod.setAccessible(true);
|
||||
selectFirstDiffMethod.invoke(this, true);
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
} else {
|
||||
super.copy(leftToRight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.maven.pom;
|
||||
|
||||
import org.eclipse.compare.CompareConfiguration;
|
||||
import org.eclipse.compare.IViewerCreator;
|
||||
import org.eclipse.jface.viewers.Viewer;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
|
||||
public class PomMergeViewerCreator implements IViewerCreator {
|
||||
|
||||
public PomMergeViewerCreator() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Viewer createViewer(Composite parent, CompareConfiguration config) {
|
||||
return new PomMergeViewer(parent, 0, config);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.maven.pom;
|
||||
|
||||
import org.eclipse.ui.plugin.AbstractUIPlugin;
|
||||
|
||||
public class PomPlugin extends AbstractUIPlugin {
|
||||
|
||||
public static final String PLUGIN_ID = "org.springframework.ide.eclipse.maven.pom"; //$NON-NLS-1$
|
||||
|
||||
public static final String POM_STRUCTURE_ADDITIONS_COMPARE_SETTING = "Pom-Structure-Additions-Only";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.maven.pom;
|
||||
|
||||
import org.eclipse.core.runtime.Assert;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.springframework.ide.eclipse.maven.pom.DomStructureComparable.DomType;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.Locator;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
public class SaxToStructureHandler extends DefaultHandler {
|
||||
|
||||
private DomStructureComparable.Builder current;
|
||||
private Locator locator;
|
||||
private DomStructureComparable root;
|
||||
private IDocument document;
|
||||
private IdProviderRegistry idProviders;
|
||||
|
||||
public SaxToStructureHandler(IDocument document, IdProviderRegistry idProviders) {
|
||||
this.document = document;
|
||||
this.idProviders = idProviders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDocumentLocator(Locator locator) {
|
||||
this.locator = locator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDocument() throws SAXException {
|
||||
Assert.isTrue(root == null);
|
||||
current = DomStructureComparable.createRoot(document);
|
||||
current.start(locator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endDocument() throws SAXException {
|
||||
Assert.isTrue(current.type == DomType.ROOT);
|
||||
current.end(locator);
|
||||
root = current.build("root");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||
current = DomStructureComparable.createElement(document, current, localName);
|
||||
current.start(locator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName) throws SAXException {
|
||||
current.end(locator);
|
||||
DomStructureComparable child = current.build(idProviders.getId(current));
|
||||
current = current.parent;
|
||||
current.addChild(child);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void characters(char[] ch, int start, int length) throws SAXException {
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
// for (int i = 0; i < length; i++) {
|
||||
// sb.append(ch[start + i]);
|
||||
// }
|
||||
// DomStructureComparable.createText(document, current);
|
||||
// System.out.println("CHARACTERS: '" + sb + "'");
|
||||
// }
|
||||
|
||||
public DomStructureComparable getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.maven.pom;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
import org.eclipse.compare.IEditableContent;
|
||||
import org.eclipse.compare.IEncodedStreamContentAccessor;
|
||||
import org.eclipse.compare.IStreamContentAccessor;
|
||||
import org.eclipse.compare.structuremergeviewer.Differencer;
|
||||
import org.eclipse.compare.structuremergeviewer.IDiffContainer;
|
||||
import org.eclipse.compare.structuremergeviewer.IStructureComparator;
|
||||
import org.eclipse.compare.structuremergeviewer.IStructureCreator;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.jface.text.Document;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.springsource.ide.eclipse.commons.livexp.util.Log;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
/**
|
||||
* Implementation is based on the Team XML Compare example
|
||||
*
|
||||
* @author Alex Boyko
|
||||
*
|
||||
*/
|
||||
public class XMLStructureCreator implements IStructureCreator {
|
||||
|
||||
protected static final boolean DEBUG_MODE= false;
|
||||
|
||||
public static final String DEFAULT_NAME= "XML Compare";
|
||||
|
||||
private boolean fRemoveWhiteSpace;
|
||||
|
||||
final private IdProviderRegistry idProviders;
|
||||
|
||||
public XMLStructureCreator(IdProviderRegistry idProviders) {
|
||||
this.idProviders = idProviders;
|
||||
fRemoveWhiteSpace= false;
|
||||
}
|
||||
|
||||
/*
|
||||
* This title will be shown in the title bar of the structure compare pane.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return DEFAULT_NAME;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the XML parse tree of the input.
|
||||
*/
|
||||
@Override
|
||||
public IStructureComparator getStructure(Object input) {
|
||||
if (XMLStructureCreator.DEBUG_MODE)
|
||||
System.out.println("Starting parse"); //$NON-NLS-1$
|
||||
|
||||
if (!(input instanceof IStreamContentAccessor))
|
||||
return null;
|
||||
|
||||
IStreamContentAccessor sca= (IStreamContentAccessor) input;
|
||||
|
||||
try {
|
||||
// Input parsed with parser.parse(new InputSource(sca.getContents));
|
||||
|
||||
String contents= readString(sca);
|
||||
if (contents == null)
|
||||
contents= ""; //$NON-NLS-1$
|
||||
return createStructure(new Document(contents));
|
||||
} catch (CoreException e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public DomStructureComparable createStructure(IDocument doc) {
|
||||
try {
|
||||
|
||||
SAXParserFactory factory= SAXParserFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
SAXParser parser= factory.newSAXParser();
|
||||
SaxToStructureHandler handler = new SaxToStructureHandler(doc, idProviders);
|
||||
if (doc.getLength() == 0) {
|
||||
return null;
|
||||
}
|
||||
parser.parse(new InputSource(new StringReader(doc.get())), handler);
|
||||
|
||||
if (XMLStructureCreator.DEBUG_MODE)
|
||||
System.out.println("End of parse"); //$NON-NLS-1$
|
||||
|
||||
return handler.getRoot();
|
||||
} catch (SAXParseException e) {
|
||||
Log.log(e);
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
// MessageDialog.openError(PomPlugin.getActiveWorkbenchShell(),"Error in XML parser","An error occured in the XML parser.\nNo structured compare can be shown");
|
||||
Log.log(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canSave() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean canRewriteTree() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void rewriteTree(Differencer differencer, IDiffContainer root) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(IStructureComparator structure, Object input) {
|
||||
if (input instanceof IEditableContent && structure instanceof DomStructureComparable) {
|
||||
IDocument document= ((DomStructureComparable) structure).getDocument();
|
||||
IEditableContent bca= (IEditableContent) input;
|
||||
String contents= document.get();
|
||||
String encoding= null;
|
||||
if (input instanceof IEncodedStreamContentAccessor) {
|
||||
try {
|
||||
encoding= ((IEncodedStreamContentAccessor)input).getCharset();
|
||||
} catch (CoreException e1) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (encoding == null)
|
||||
encoding= "UTF-8"; //$NON-NLS-1$
|
||||
try {
|
||||
bca.setContent(contents.getBytes(encoding));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
bca.setContent(contents.getBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContents(Object node, boolean ignoreWhitespace) {
|
||||
if (node instanceof DomStructureComparable) {
|
||||
String s = ((DomStructureComparable)node).getTextContent();
|
||||
if (ignoreWhitespace)
|
||||
s= s.trim();
|
||||
return s;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStructureComparator locate(Object path, Object source) {
|
||||
return null;
|
||||
}
|
||||
|
||||
static String readString(IStreamContentAccessor sa) throws CoreException {
|
||||
InputStream is= sa.getContents();
|
||||
String encoding= null;
|
||||
if (sa instanceof IEncodedStreamContentAccessor)
|
||||
encoding= ((IEncodedStreamContentAccessor) sa).getCharset();
|
||||
if (encoding == null)
|
||||
encoding= "UTF-8"; //$NON-NLS-1$
|
||||
return readString(is, encoding);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns null if an error occurred.
|
||||
*/
|
||||
private static String readString(InputStream is, String encoding) {
|
||||
if (is == null)
|
||||
return null;
|
||||
BufferedReader reader= null;
|
||||
try {
|
||||
StringBuilder buffer= new StringBuilder();
|
||||
char[] part= new char[2048];
|
||||
int read= 0;
|
||||
reader= new BufferedReader(new InputStreamReader(is, encoding));
|
||||
|
||||
while ((read= reader.read(part)) != -1)
|
||||
buffer.append(part, 0, read);
|
||||
|
||||
return buffer.toString();
|
||||
|
||||
} catch (IOException ex) {
|
||||
// NeedWork
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException ex) {
|
||||
// silently ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean isWhiteSpace(char c) {
|
||||
return c == '\t' || c == '\n' || c == '\r' || c == ' ';
|
||||
}
|
||||
|
||||
protected String removeWhiteSpace(String str) {
|
||||
str= trimWhiteSpace(str);
|
||||
StringBuilder retStr= new StringBuilder();
|
||||
int start= 0, end= 0;
|
||||
outer_while: while (true) {
|
||||
while (end < str.length() && !isWhiteSpace(str.charAt(end))) {
|
||||
end++;
|
||||
}
|
||||
if (end > str.length())
|
||||
break outer_while;
|
||||
if (start != 0)
|
||||
retStr.append(' ');
|
||||
retStr.append(str.substring(start, end));
|
||||
end++;
|
||||
while (end < str.length() && isWhiteSpace(str.charAt(end))) {
|
||||
end++;
|
||||
}
|
||||
start= end;
|
||||
}
|
||||
return retStr.toString();
|
||||
}
|
||||
|
||||
protected String trimWhiteSpace(String str) {
|
||||
int start= 0, end= str.length() - 1;
|
||||
while (start < str.length() && isWhiteSpace(str.charAt(start))) {
|
||||
start++;
|
||||
}
|
||||
if (start == str.length())
|
||||
return ""; //$NON-NLS-1$
|
||||
while (end >= 0 && isWhiteSpace(str.charAt(end))) {
|
||||
end--;
|
||||
}
|
||||
return str.substring(start, end + 1);
|
||||
}
|
||||
|
||||
public void setRemoveWhiteSpace(boolean removeWhiteSpace) {
|
||||
fRemoveWhiteSpace= removeWhiteSpace;
|
||||
}
|
||||
|
||||
public boolean getRemoveWhiteSpace() {
|
||||
return fRemoveWhiteSpace;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.maven.pom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.eclipse.compare.structuremergeviewer.DiffNode;
|
||||
import org.eclipse.compare.structuremergeviewer.Differencer;
|
||||
import org.eclipse.compare.structuremergeviewer.IDiffContainer;
|
||||
import org.eclipse.compare.structuremergeviewer.IDiffElement;
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.Position;
|
||||
import org.springsource.ide.eclipse.commons.frameworks.core.util.DocumentRegion;
|
||||
|
||||
public class XmlDocumentDiffer {
|
||||
|
||||
private IDocument lDoc;
|
||||
private IDocument rDoc;
|
||||
private IdProviderRegistry idProviders;
|
||||
private Predicate<Difference> filter = x -> true;
|
||||
|
||||
public XmlDocumentDiffer(IDocument document1, IDocument document2) {
|
||||
this.idProviders = new IdProviderRegistry();
|
||||
this.lDoc = document1;
|
||||
this.rDoc = document2;
|
||||
}
|
||||
|
||||
public List<Difference> getDiffs() {
|
||||
XMLStructureCreator structureCreator = new XMLStructureCreator(idProviders);
|
||||
DomStructureComparable leftStructure = structureCreator.createStructure(lDoc);
|
||||
|
||||
DomStructureComparable rightStructure = structureCreator.createStructure(rDoc);
|
||||
|
||||
if (lDoc == null || rDoc == null)
|
||||
return Collections.emptyList();
|
||||
|
||||
if (leftStructure == null && rightStructure == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (leftStructure == null && rightStructure != null) {
|
||||
List<Difference> diffs = new ArrayList<>();
|
||||
diffs.add(new Difference(Direction.LEFT, new Position(0, 0), new Position(0, rDoc.getLength()), leftStructure, rightStructure));
|
||||
return diffs;
|
||||
}
|
||||
|
||||
if (leftStructure != null && rightStructure == null) {
|
||||
List<Difference> diffs = new ArrayList<>();
|
||||
diffs.add(new Difference(Direction.RIGHT, new Position(0, lDoc.getLength()), new Position(0, 0), leftStructure, rightStructure));
|
||||
return diffs;
|
||||
}
|
||||
|
||||
Differencer differencer = new Differencer() {
|
||||
@Override
|
||||
protected boolean contentsEqual(Object o1,
|
||||
char contributor1, Object o2, char contributor2) {
|
||||
boolean ignoreWhiteSpace = true;
|
||||
String s1, s2;
|
||||
if (hasChildren(o1) && hasChildren(o2)) {
|
||||
return true;
|
||||
}
|
||||
s1 = structureCreator.getContents(o1, ignoreWhiteSpace);
|
||||
s2 = structureCreator.getContents(o2, ignoreWhiteSpace);
|
||||
if (s1 == null || s2 == null)
|
||||
return false;
|
||||
return s1.equals(s2);
|
||||
}
|
||||
private boolean hasChildren(Object o) {
|
||||
if (o instanceof DomStructureComparable) {
|
||||
return ((DomStructureComparable)o).getChildren().length > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
DiffNode differences = (DiffNode) differencer.findDifferences(false, new NullProgressMonitor(), null, null, leftStructure, rightStructure);
|
||||
List<IDiffElement> leafs = new ArrayList<>();
|
||||
gatherLeafs(differences, leafs);
|
||||
|
||||
List<Difference> allDiffs = new ArrayList<>();
|
||||
|
||||
if (leafs.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
for (IDiffElement e : leafs) {
|
||||
DiffNode diffNode = (DiffNode) e;
|
||||
DomStructureComparable left = (DomStructureComparable) diffNode.getLeft();
|
||||
DomStructureComparable right = (DomStructureComparable) diffNode.getRight();
|
||||
Difference difference = null;
|
||||
DiffNode parentNode;
|
||||
Position insertLocation;
|
||||
switch (diffNode.getKind() & Differencer.CHANGE_TYPE_MASK) {
|
||||
case Differencer.CHANGE:
|
||||
difference = new Difference(Direction.BOTH, left.getRange(), right.getRange(), left, right);
|
||||
break;
|
||||
case Differencer.ADDITION:
|
||||
parentNode = (DiffNode) diffNode.getParent();
|
||||
insertLocation = parentNode == null ? new Position(lDoc.getLength(), 0) : insertPosition((DomStructureComparable)parentNode.getLeft());
|
||||
difference = new Difference(Direction.LEFT, insertLocation, right.getRange(), left, right);
|
||||
break;
|
||||
case Differencer.DELETION:
|
||||
parentNode = (DiffNode) diffNode.getParent();
|
||||
insertLocation = parentNode == null ? new Position(rDoc.getLength(), 0) : insertPosition((DomStructureComparable)parentNode.getRight());
|
||||
difference = new Difference(Direction.RIGHT, left.getRange(), insertLocation, left, right);
|
||||
break;
|
||||
}
|
||||
if (this.filter.test(difference)) {
|
||||
allDiffs.add(difference);
|
||||
}
|
||||
}
|
||||
|
||||
List<Position> leftChanges = new ArrayList<>();
|
||||
List<Position> rightChanges = new ArrayList<>();
|
||||
|
||||
for (Difference d : allDiffs) {
|
||||
leftChanges.add(d.leftRange);
|
||||
rightChanges.add(d.rightRange);
|
||||
}
|
||||
|
||||
Comparator<Position> positionComparator = new Comparator<Position>() {
|
||||
|
||||
@Override
|
||||
public int compare(Position o1, Position o2) {
|
||||
return o1.offset - o2.offset;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Collections.sort(leftChanges, positionComparator);
|
||||
Collections.sort(rightChanges, positionComparator);
|
||||
|
||||
int prevLeftEnd = 0;
|
||||
int prevRightEnd = 0;
|
||||
for (int i = 0; i < leftChanges.size(); i++) {
|
||||
Position leftChange = leftChanges.get(i);
|
||||
Position rightChange = rightChanges.get(i);
|
||||
allDiffs.add(new Difference(Direction.NONE, new Position(prevLeftEnd, leftChange.offset - prevLeftEnd),
|
||||
new Position(prevRightEnd, rightChange.offset - prevRightEnd), null, null));
|
||||
prevLeftEnd = leftChange.offset + leftChange.length;
|
||||
prevRightEnd = rightChange.offset + rightChange.length;
|
||||
}
|
||||
if (prevLeftEnd < lDoc.getLength() || prevRightEnd < rDoc.getLength()) {
|
||||
allDiffs.add(new Difference(
|
||||
Direction.NONE,
|
||||
new Position(prevLeftEnd, lDoc.getLength() - prevLeftEnd),
|
||||
new Position(prevRightEnd, rDoc.getLength() - prevRightEnd),
|
||||
null,
|
||||
null
|
||||
));
|
||||
}
|
||||
|
||||
Collections.sort(allDiffs, new Comparator<Difference>() {
|
||||
|
||||
@Override
|
||||
public int compare(Difference o1, Difference o2) {
|
||||
int compareLeft = positionComparator.compare(o1.leftRange, o2.leftRange);
|
||||
if (compareLeft == 0) {
|
||||
return positionComparator.compare(o1.rightRange, o2.rightRange);
|
||||
} else {
|
||||
return compareLeft;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return allDiffs;
|
||||
}
|
||||
|
||||
private Position insertPosition(DomStructureComparable parent) {
|
||||
int end = parent.getRange().offset + parent.getRange().length - 1;
|
||||
DocumentRegion region = new DocumentRegion(parent.getDocument(), end, end);
|
||||
region = region.extendBeforeUntil('<');
|
||||
DocumentRegion before = region.textBeforeOnLine();
|
||||
if (before.isWhiteSpace()) {
|
||||
return new Position(before.getStart(), 0);
|
||||
} else {
|
||||
return new Position(region.getStart(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void gatherLeafs(IDiffElement node, List<IDiffElement> leafs) {
|
||||
if (node instanceof IDiffContainer) {
|
||||
IDiffContainer container = (IDiffContainer) node;
|
||||
if (container.hasChildren()) {
|
||||
for (IDiffElement child : container.getChildren()) {
|
||||
gatherLeafs(child, leafs);
|
||||
}
|
||||
} else {
|
||||
leafs.add(node);
|
||||
}
|
||||
} else if (node != null){
|
||||
leafs.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Difference {
|
||||
final public Direction direction;
|
||||
final public Position leftRange;
|
||||
final public Position rightRange;
|
||||
final public DomStructureComparable leftComparable;
|
||||
final public DomStructureComparable rightComparable;
|
||||
public Difference(Direction direction, Position leftRange, Position rightRange, DomStructureComparable left, DomStructureComparable right) {
|
||||
this.direction = direction;
|
||||
this.leftRange = leftRange;
|
||||
this.rightRange = rightRange;
|
||||
this.leftComparable = left;
|
||||
this.rightComparable = right;
|
||||
}
|
||||
}
|
||||
|
||||
public static enum Direction {
|
||||
NONE(Differencer.NO_CHANGE),
|
||||
LEFT(Differencer.ADDITION), // inserted on the left
|
||||
RIGHT(Differencer.DELETION), // inserted on the right
|
||||
BOTH(Differencer.CHANGE);
|
||||
|
||||
final int differencerKind;
|
||||
|
||||
private Direction(int eclipseKind) {
|
||||
this.differencerKind = eclipseKind;
|
||||
}
|
||||
|
||||
int getDifferencerKind() {
|
||||
return differencerKind;
|
||||
}
|
||||
}
|
||||
|
||||
public XmlDocumentDiffer idProvider(Predicate<DomStructureComparable.Builder> selector, IdProvider provider) {
|
||||
this.idProviders.add(selector, provider);
|
||||
return this;
|
||||
}
|
||||
|
||||
public XmlDocumentDiffer filter(Predicate<Difference> filter) {
|
||||
this.filter = filter;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.maven.pom;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.ide.eclipse.maven.pom.DomStructureComparable.Builder;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
public class XmlSelectors {
|
||||
|
||||
public static Predicate<DomStructureComparable.Builder> childrenOf(String... _tagNames) {
|
||||
ImmutableSet<String> tagNames = ImmutableSet.copyOf(_tagNames);
|
||||
return new Predicate<DomStructureComparable.Builder>() {
|
||||
|
||||
@Override
|
||||
public boolean test(DomStructureComparable.Builder n) {
|
||||
return n.parent != null && tagNames.contains(n.parent.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "childrenOf(" + tagNames + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Predicate<Builder> tagName(String tagName) {
|
||||
return new Predicate<DomStructureComparable.Builder>() {
|
||||
|
||||
@Override
|
||||
public boolean test(Builder n) {
|
||||
return tagName.equals(n.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "tagName(" + tagName + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Predicate<Builder> grandParent(String tagName) {
|
||||
return ancestor(2, tagName);
|
||||
}
|
||||
|
||||
public static Predicate<Builder> ancestor(int level, String tagName) {
|
||||
return new Predicate<DomStructureComparable.Builder>() {
|
||||
|
||||
@Override
|
||||
public boolean test(Builder n) {
|
||||
Builder ancestor = getAncestor(level, n);
|
||||
return ancestor != null && tagName.equals(ancestor.name);
|
||||
}
|
||||
|
||||
private Builder getAncestor(int level, Builder n) {
|
||||
if (level == 0) {
|
||||
return n;
|
||||
} else {
|
||||
return n == null ? null : getAncestor(level - 1, n.parent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ancestor(" + level + ", "+ tagName + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Predicate<Builder> path(String... tagNames) {
|
||||
return new Predicate<Builder>() {
|
||||
|
||||
@Override
|
||||
public boolean test(Builder t) {
|
||||
int i = tagNames.length - 1;
|
||||
Builder current = t;
|
||||
while (i >= 0) {
|
||||
String expectTag = tagNames[i];
|
||||
if (current == null || !match(expectTag, current.name)) {
|
||||
return false;
|
||||
}
|
||||
i--;
|
||||
current = current.parent;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean match(String expectTag, String name) {
|
||||
return "*".equals(expectTag) || expectTag.equals(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("path(");
|
||||
boolean first = true;
|
||||
for (String t : tagNames) {
|
||||
if (!first) {
|
||||
sb.append(" -> ");
|
||||
}
|
||||
sb.append(t);
|
||||
first = false;
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user