Commit f162c881 authored by Konstantin Kolosovsky's avatar Konstantin Kolosovsky
Browse files

svn: Use jaxb for "svn info" output parsing

parent 14787aa9
Showing with 186 additions and 1039 deletions
+186 -1039
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.idea.svn.api;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.xml.bind.annotation.XmlEnumValue;
import java.util.Map;
public enum Depth {
UNKNOWN("unknown"),
INFINITY("infinity"),
IMMEDIATES("immediates"),
FILES("files"),
EMPTY("empty"),
EXCLUDE("exclude");
@XmlEnumValue("") UNKNOWN("unknown"),
@XmlEnumValue("infinity") INFINITY("infinity"),
@XmlEnumValue("immediates") IMMEDIATES("immediates"),
@XmlEnumValue("files") FILES("files"),
@XmlEnumValue("empty") EMPTY("empty"),
@XmlEnumValue("exclude") EXCLUDE("exclude");
@NotNull private static final Map<String, Depth> ourAllDepths = ContainerUtil.newHashMap();
......
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.idea.svn.conflict;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import javax.xml.bind.annotation.XmlEnumValue;
import java.util.Map;
/**
* @author Konstantin Kolosovsky.
*/
public enum ConflictAction {
EDIT("edit", "edited"),
ADD("add", "added"),
DELETE("delete", "deleted"),
REPLACE("replace", "replaced");
@XmlEnumValue("edit") EDIT("edit", "edited"),
@XmlEnumValue("add") ADD("add", "added"),
@XmlEnumValue("delete") DELETE("delete", "deleted"),
@XmlEnumValue("replace") REPLACE("replace", "replaced");
@NotNull private static final Map<String, ConflictAction> ourAllActions = ContainerUtil.newHashMap();
......
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.idea.svn.conflict;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
/**
* @author Konstantin Kolosovsky.
*/
import javax.xml.bind.annotation.XmlEnumValue;
public enum ConflictOperation {
NONE,
UPDATE,
SWITCH,
MERGE;
@XmlEnumValue("") NONE,
@XmlEnumValue("update") UPDATE,
@XmlEnumValue("switch") SWITCH,
@XmlEnumValue("merge") MERGE;
@NotNull
public static ConflictOperation from(@NotNull @NonNls String operationName) {
......
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.idea.svn.conflict;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import javax.xml.bind.annotation.XmlEnumValue;
import java.util.Map;
/**
* @author Konstantin Kolosovsky.
*/
public enum ConflictReason {
EDITED("edit", "edited"),
OBSTRUCTED("obstruction", "obstruct", "obstructed"),
DELETED("delete", "deleted"),
MISSING("missing", "miss"),
UNVERSIONED("unversioned", "unversion"),
@XmlEnumValue("edit") EDITED("edit", "edited"),
@XmlEnumValue("obstruction") OBSTRUCTED("obstruction", "obstruct", "obstructed"),
@XmlEnumValue("delete") DELETED("delete", "deleted"),
@XmlEnumValue("missing") MISSING("missing", "miss"),
@XmlEnumValue("unversioned") UNVERSIONED("unversioned", "unversion"),
/**
* @since 1.6
*/
ADDED("add", "added"),
@XmlEnumValue("add") ADDED("add", "added"),
/**
* @since 1.7
*/
REPLACED("replace", "replaced"),
@XmlEnumValue("replace") REPLACED("replace", "replaced"),
/**
* @since 1.8
*/
MOVED_AWAY("moved-away"),
MOVED_HERE("moved-here");
@XmlEnumValue("moved-away") MOVED_AWAY("moved-away"),
@XmlEnumValue("moved-here") MOVED_HERE("moved-here");
@NotNull private static final Map<String, ConflictReason> ourAllReasons = ContainerUtil.newHashMap();
......
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.idea.svn.conflict
import org.jetbrains.idea.svn.SvnUtil.createUrl
import org.jetbrains.idea.svn.SvnUtil.resolvePath
import org.jetbrains.idea.svn.api.BaseNodeDescription
import org.jetbrains.idea.svn.api.NodeKind
import java.io.File
import javax.xml.bind.annotation.*
class TreeConflictDescription private constructor(builder: Builder, base: File) : BaseNodeDescription(builder.kind) {
val path = resolvePath(base, builder.path)
val conflictAction = builder.action
val conflictReason = builder.reason
val operation = builder.operation
val sourceLeftVersion = builder.sourceLeftVersion
val sourceRightVersion = builder.sourceRightVersion
class TreeConflictDescription(val path: File,
nodeKind: NodeKind,
val conflictAction: ConflictAction,
val conflictReason: ConflictReason,
val operation: ConflictOperation,
val sourceLeftVersion: ConflictVersion?,
val sourceRightVersion: ConflictVersion?) : BaseNodeDescription(nodeKind) {
fun toPresentableString() = "local $conflictReason, incoming $conflictAction upon $operation"
@XmlAccessorType(XmlAccessType.NONE)
@XmlType(name = "tree-conflict")
@XmlRootElement(name = "tree-conflict")
class Builder {
@XmlAttribute(name = "victim")
var path = ""
@XmlAttribute
var kind = NodeKind.UNKNOWN
@XmlAttribute
var operation = ConflictOperation.NONE
@XmlAttribute
var action = ConflictAction.ADD
@XmlAttribute
var reason = ConflictReason.ADDED
@XmlElement(name = "version")
private val versions = mutableListOf<ConflictVersionWithSide>()
val sourceLeftVersion get() = versions.find { it.side == "source-left" }?.build()
val sourceRightVersion get() = versions.find { it.side == "source-right" }?.build()
fun build(base: File) = TreeConflictDescription(this, base)
}
}
@XmlAccessorType(XmlAccessType.NONE)
@XmlType(name = "version")
@XmlRootElement(name = "version")
private class ConflictVersionWithSide {
@XmlAttribute
var side = ""
@XmlAttribute
var kind = NodeKind.UNKNOWN
@XmlAttribute(name = "path-in-repos")
var path = ""
@XmlAttribute(name = "repos-url")
var repositoryRoot = ""
@XmlAttribute(name = "revision")
var revisionNumber = -1L
fun build() = ConflictVersion(createUrl(repositoryRoot), path, revisionNumber, kind)
}
\ No newline at end of file
......@@ -6,56 +6,45 @@ import com.intellij.execution.process.ProcessOutputTypes
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.text.StringUtil.isEmptyOrSpaces
import com.intellij.openapi.vfs.CharsetToolkit
import org.jetbrains.idea.svn.api.BaseSvnClient
import org.jetbrains.idea.svn.api.Depth
import org.jetbrains.idea.svn.api.Revision
import org.jetbrains.idea.svn.SvnUtil.createUrl
import org.jetbrains.idea.svn.SvnUtil.resolvePath
import org.jetbrains.idea.svn.api.*
import org.jetbrains.idea.svn.api.Target
import org.jetbrains.idea.svn.commandLine.*
import org.jetbrains.idea.svn.checkin.CommitInfo
import org.jetbrains.idea.svn.commandLine.CommandUtil
import org.jetbrains.idea.svn.commandLine.CommandUtil.parse
import org.jetbrains.idea.svn.commandLine.CommandUtil.requireExistingParent
import org.xml.sax.SAXException
import java.io.ByteArrayInputStream
import org.jetbrains.idea.svn.commandLine.LineCommandAdapter
import org.jetbrains.idea.svn.commandLine.SvnBindException
import org.jetbrains.idea.svn.commandLine.SvnCommandName
import org.jetbrains.idea.svn.conflict.TreeConflictDescription
import org.jetbrains.idea.svn.lock.Lock
import java.io.File
import java.io.IOException
import javax.xml.parsers.ParserConfigurationException
import javax.xml.parsers.SAXParserFactory
import javax.xml.bind.JAXBException
import javax.xml.bind.annotation.*
private val LOG = logger<CmdInfoClient>()
private fun parseResult(handler: InfoConsumer, base: File?, result: String?) {
if (isEmptyOrSpaces(result)) return
val infoHandler = SvnInfoHandler(base) {
try {
handler.consume(it)
}
catch (e: SvnBindException) {
throw SvnExceptionWrapper(e)
}
}
private fun parseResult(handler: InfoConsumer, base: File?, result: String) {
try {
val infoRoot = parse(result, InfoRoot::class.java)
parseResult(result!!, infoHandler)
}
if (infoRoot != null) {
for (entry in infoRoot.entries) {
val file = base?.let { resolvePath(it, entry.path) }
val url = entry.url?.let { createUrl(it) }
val repositoryRootUrl = entry.repository?.root?.let { createUrl(it) }
val copyFromUrl = entry.workingCopyInfo?.copyFromUrl?.let { createUrl(it) }
private fun parseResult(result: String, handler: SvnInfoHandler) {
try {
SAXParserFactory.newInstance().newSAXParser().parse(
ByteArrayInputStream(result.trim { it <= ' ' }.toByteArray(CharsetToolkit.UTF8_CHARSET)), handler)
}
catch (e: SvnExceptionWrapper) {
LOG.info("info output $result")
throw SvnBindException(e.cause)
}
catch (e: IOException) {
LOG.info("info output $result")
throw SvnBindException(e)
}
catch (e: SAXException) {
LOG.info("info output $result")
throw SvnBindException(e)
val info = Info(file, url, Revision.of(entry.revisionNumber), entry.nodeKind, repositoryRootUrl, entry.repository?.uuid,
entry.commit?.build(), entry.workingCopyInfo?.schedule, entry.workingCopyInfo?.depth, copyFromUrl,
Revision.of(entry.workingCopyInfo?.copyFromRevision ?: -1L), entry.lock?.build(), entry.conflict?.previousBaseFile,
entry.conflict?.currentBaseFile, entry.conflict?.previousWorkingCopyFile, entry.treeConflict?.build(base!!))
handler.consume(info)
}
}
}
catch (e: ParserConfigurationException) {
catch (e: JAXBException) {
LOG.info("info output $result")
throw SvnBindException(e)
}
......@@ -133,10 +122,72 @@ class CmdInfoClient : BaseSvnClient(), InfoClient {
}
companion object {
fun parseResult(base: File?, result: String?): Info? {
fun parseResult(base: File?, result: String): Info? {
val ref = Ref<Info?>()
parseResult(InfoConsumer(ref::set), base, result)
return ref.get()
}
}
}
@XmlRootElement(name = "info")
@XmlAccessorType(XmlAccessType.NONE)
private class InfoRoot {
@XmlElement(name = "entry")
val entries = mutableListOf<Entry>()
}
@XmlAccessorType(XmlAccessType.FIELD)
private class Entry {
@XmlAttribute(required = true)
var path = ""
@XmlAttribute(name = "kind", required = true)
var nodeKind = NodeKind.UNKNOWN
@XmlAttribute(name = "revision", required = true)
var revisionNumber = -1L
var url: String? = null
var repository: Repository? = null
@XmlElement(name = "wc-info")
var workingCopyInfo: WorkingCopyInfo? = null
var commit: CommitInfo.Builder? = null
var lock: Lock.Builder? = null
var conflict: Conflict? = null
@XmlElement(name = "tree-conflict")
var treeConflict: TreeConflictDescription.Builder? = null
}
@XmlAccessorType(XmlAccessType.FIELD)
private class Repository {
var root: String? = null
var uuid: String? = null
}
@XmlAccessorType(XmlAccessType.FIELD)
private class WorkingCopyInfo {
var schedule: String? = null
var depth: Depth = Depth.UNKNOWN
@XmlElement(name = "copy-from-url")
var copyFromUrl: String? = null
@XmlElement(name = "copy-from-rev")
var copyFromRevision = -1L
}
@XmlAccessorType(XmlAccessType.NONE)
private class Conflict {
@XmlElement(name = "prev-base-file", required = true)
var previousBaseFile = ""
@XmlElement(name = "prev-wc-file")
var previousWorkingCopyFile: String? = null
@XmlElement(name = "cur-base-file", required = true)
var currentBaseFile = ""
}
\ No newline at end of file
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.idea.svn.info;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnUtil;
import org.jetbrains.idea.svn.api.NodeKind;
import org.jetbrains.idea.svn.checkin.CommitInfo;
import org.jetbrains.idea.svn.commandLine.SvnBindException;
import org.jetbrains.idea.svn.lock.Lock;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.io.File;
import java.util.*;
import static com.intellij.openapi.util.text.StringUtil.notNullize;
import static org.jetbrains.idea.svn.SvnUtil.createUrl;
public class SvnInfoHandler extends DefaultHandler {
@Nullable private final File myBase;
private final Consumer<? super org.jetbrains.idea.svn.info.Info> myInfoConsumer;
private final Map<File, org.jetbrains.idea.svn.info.Info> myResultsMap;
private SvnInfoStructure myPending;
private final Map<String, Getter<ElementHandlerBase>> myElementsMap;
private final List<ElementHandlerBase> myParseStack;
private final StringBuilder mySb;
public SvnInfoHandler(@Nullable File base, final Consumer<? super org.jetbrains.idea.svn.info.Info> infoConsumer) {
myBase = base;
myInfoConsumer = infoConsumer;
myPending = createPending();
myElementsMap = new HashMap<>();
fillElements();
myParseStack = new ArrayList<>();
myParseStack.add(new Fake());
myResultsMap = new HashMap<>();
mySb = new StringBuilder();
}
private void switchPending() throws SAXException {
final org.jetbrains.idea.svn.info.Info info;
try {
info = myPending.convert();
}
catch (SvnBindException e) {
throw new SAXException(e);
}
if (myInfoConsumer != null) {
myInfoConsumer.consume(info);
}
myResultsMap.put(info.getFile(), info);
myPending = createPending();
}
private SvnInfoStructure createPending() {
SvnInfoStructure pending = new SvnInfoStructure();
pending.myDepth = org.jetbrains.idea.svn.api.Depth.INFINITY;
return pending;
}
@Override
public void endDocument() throws SAXException {
assertSAX(! myParseStack.isEmpty());
for (int i = myParseStack.size() - 1; i >= 0; -- i) {
ElementHandlerBase current = myParseStack.get(i);
if (current instanceof Entry) {
switchPending();
break;
}
}
myParseStack.clear();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
assertSAX(! myParseStack.isEmpty());
ElementHandlerBase current = myParseStack.get(myParseStack.size() - 1);
while (true) {
final boolean createNewChild = current.startElement(uri, localName, qName, attributes);
if (createNewChild) {
assertSAX(myElementsMap.containsKey(qName));
final ElementHandlerBase newChild = myElementsMap.get(qName).get();
newChild.setParent(current);
newChild.updateInfo(attributes, myPending);
myParseStack.add(newChild);
return;
} else {
// go up
if (current instanceof Entry) {
switchPending();
}
myParseStack.remove(myParseStack.size() - 1);
assertSAX(! myParseStack.isEmpty());
current = myParseStack.get(myParseStack.size() - 1);
}
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
ElementHandlerBase current = myParseStack.get(myParseStack.size() - 1);
String value = mySb.toString().trim();
if (!StringUtil.isEmpty(value)) {
current.characters(value, myPending);
}
mySb.setLength(0);
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
assertSAX(! myParseStack.isEmpty());
mySb.append(ch, start, length);
}
public Map<String, Getter<ElementHandlerBase>> getElementsMap() {
return myElementsMap;
}
private void fillElements() {
myElementsMap.put("copy-from-url", () -> new CopyFromUrl());
myElementsMap.put("copy-from-rev", () -> new CopyFromRev());
myElementsMap.put("changelist", () -> new ChangeList());
myElementsMap.put("author", () -> new Author());
myElementsMap.put("checksum", () -> new Checksum());
myElementsMap.put("commit", () -> new Commit());
myElementsMap.put("conflict", () -> new Conflict());
myElementsMap.put("cur-base-file", () -> new CurBase());
myElementsMap.put("date", () -> new Date());
myElementsMap.put("depth", () -> new Depth());
myElementsMap.put("entry", () -> new Entry(myBase));
myElementsMap.put("info", () -> new Info());
myElementsMap.put("prev-base-file", () -> new PrevBase());
myElementsMap.put("prev-wc-file", () -> new PrevWc());
myElementsMap.put("prop-file", () -> new PropFile());
myElementsMap.put("repository", () -> new Repository());
myElementsMap.put("root", () -> new Root());
myElementsMap.put("schedule", () -> new Schedule());
myElementsMap.put("text-updated", () -> new TextUpdated());
myElementsMap.put("tree-conflict", () -> new TreeConflict());
myElementsMap.put("url", () -> new Url());
myElementsMap.put("relative-url", () -> new RelativeUrl());
myElementsMap.put("lock", () -> new LockElement());
myElementsMap.put("token", () -> new LockToken());
myElementsMap.put("owner", () -> new LockOwner());
myElementsMap.put("comment", () -> new LockComment());
myElementsMap.put("created", () -> new LockCreated());
myElementsMap.put("uuid", () -> new Uuid());
myElementsMap.put("version", () -> new Version());
myElementsMap.put("wc-info", () -> new WcInfo());
myElementsMap.put("moved-to", () -> new MovedPath());
myElementsMap.put("moved-from", () -> new MovedPath());
myElementsMap.put("wcroot-abspath", () -> new WcRoot());
}
public Map<File, org.jetbrains.idea.svn.info.Info> getResultsMap() {
return myResultsMap;
}
private static void assertSAX(final boolean shouldBeTrue) throws SAXException {
if (! shouldBeTrue) {
throw new SAXException("can not parse output");
}
}
private static class Version extends ElementHandlerBase {
private Version() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
// TODO: Currently information for conflict (not tree-conflict) available in svn 1.8 is not used
// TODO: And it also not suite well for SVNKit api
if (getParent() instanceof Conflict) {
return;
}
final String side = attributes.getValue("side");
if ("source-left".equals(side)) {
final SvnInfoStructure.ConflictVersion conflictVersion = new SvnInfoStructure.ConflictVersion();
structure.myTreeConflict.mySourceLeft = conflictVersion;
setConflictFields(attributes, conflictVersion);
} else if ("source-right".equals(side)) {
final SvnInfoStructure.ConflictVersion conflictVersion = new SvnInfoStructure.ConflictVersion();
structure.myTreeConflict.mySourceRight = conflictVersion;
setConflictFields(attributes, conflictVersion);
}
}
private void setConflictFields(Attributes attributes, SvnInfoStructure.ConflictVersion conflictVersion) {
conflictVersion.myKind = attributes.getValue("kind");
conflictVersion.myPathInRepo = attributes.getValue("path-in-repos");
conflictVersion.myRepoUrl = attributes.getValue("repos-url");
conflictVersion.myRevision = attributes.getValue("revision");
}
@Override
public void characters(String s, SvnInfoStructure structure) {
}
}
private static class TreeConflict extends ElementHandlerBase {
private TreeConflict() {
super(new String[]{}, new String[]{"version"});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
final SvnInfoStructure.TreeConflictDescription d = new SvnInfoStructure.TreeConflictDescription();
structure.myTreeConflict = d;
final String operation = attributes.getValue("operation");
if (! StringUtil.isEmptyOrSpaces(operation)) {
d.myOperation = operation;
}
final String kind = attributes.getValue("kind");
if (! StringUtil.isEmptyOrSpaces(kind)) {
d.myKind = kind;
}
final String reason = attributes.getValue("reason");
if (! StringUtil.isEmptyOrSpaces(reason)) {
d.myReason = reason;
}
final String victim = attributes.getValue("victim");
if (! StringUtil.isEmptyOrSpaces(victim)) {
d.myVictim = victim;
}
final String action = attributes.getValue("action");
if (! StringUtil.isEmptyOrSpaces(action)) {
d.myAction = action;
}
}
@Override
public void characters(String s, SvnInfoStructure structure) {
}
}
private static class PropFile extends ElementHandlerBase {
private PropFile() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
// todo check whether base should be added
structure.myPropRejectFile = s;
}
}
private static class CurBase extends ElementHandlerBase {
private CurBase() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.myConflictNew = new File(s).getName();
}
}
private static class PrevWc extends ElementHandlerBase {
private PrevWc() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.myConflictWorking = new File(s).getName();
}
}
private static class PrevBase extends ElementHandlerBase {
private PrevBase() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.myConflictOld = new File(s).getName();
}
}
private static class Conflict extends ElementHandlerBase {
private Conflict() {
super(new String[]{"prev-base-file","prev-wc-file","cur-base-file","prop-file"}, new String[]{"version"});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
}
}
private static class Date extends ElementHandlerBase {
private Date() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.myCommitInfoBuilder.setDate(SvnUtil.parseDate(s));
}
}
private static class Author extends ElementHandlerBase {
private Author() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.myCommitInfoBuilder.setAuthor(s);
}
}
private static class Commit extends ElementHandlerBase {
private Commit() {
super(new String[]{"author","date"}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException {
structure.myCommitInfoBuilder = new CommitInfo.Builder();
final String revision = attributes.getValue("revision");
try {
structure.myCommitInfoBuilder.setRevisionNumber(Long.parseLong(revision));
} catch (NumberFormatException e) {
throw new SAXException(e);
}
}
@Override
public void characters(String s, SvnInfoStructure structure) {
}
}
private static class Checksum extends ElementHandlerBase {
private Checksum() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.myChecksum = s;
}
}
/**
* "moved-from" and "moved-to" elements are represented by this class.
*/
private static class MovedPath extends ElementHandlerBase {
private MovedPath() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
// TODO: is there some field to initialize from this value?
}
}
private static class TextUpdated extends ElementHandlerBase {
private TextUpdated() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.myTextTime = s;
}
}
private static class Depth extends ElementHandlerBase {
private Depth() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.myDepth = org.jetbrains.idea.svn.api.Depth.from(s);
}
}
private static class Schedule extends ElementHandlerBase {
private Schedule() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.mySchedule = s;
}
}
private static class WcRoot extends ElementHandlerBase {
private WcRoot() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
// there is no such thing???
}
}
private static class ChangeList extends ElementHandlerBase {
private ChangeList() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.myChangelistName = s;
}
}
private static class CopyFromUrl extends ElementHandlerBase {
private CopyFromUrl() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) throws SAXException {
try {
structure.myCopyFromURL = createUrl(s);
}
catch (SvnBindException e) {
throw new SAXException(e);
}
}
}
private static class CopyFromRev extends ElementHandlerBase {
private CopyFromRev() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) throws SAXException {
try {
structure.myCopyFromRevision = Long.parseLong(s);
} catch (NumberFormatException e) {
throw new SAXException(e);
}
}
}
private static class WcInfo extends ElementHandlerBase {
private WcInfo() {
super(new String[]{"wcroot-abspath", "schedule", "depth", "text-updated", "checksum", "changelist", "copy-from-url",
"copy-from-rev", "moved-to", "moved-from"}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
}
}
private static class Uuid extends ElementHandlerBase {
private Uuid() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.myUuid = s;
}
}
private static class Root extends ElementHandlerBase {
private Root() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) throws SAXException {
try {
structure.myRootURL = createUrl(s);
}
catch (SvnBindException e) {
throw new SAXException(e);
}
}
}
private static class Repository extends ElementHandlerBase {
private Repository() {
super(new String[]{"root", "uuid"}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
}
}
private static class Url extends ElementHandlerBase {
private Url() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) throws SAXException {
try {
structure.myUrl = createUrl(s);
}
catch (SvnBindException e) {
throw new SAXException(e);
}
}
}
private static class RelativeUrl extends Url{
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.relativeUrl = s;
}
}
private static class LockElement extends ElementHandlerBase {
private LockElement() {
super(new String[]{"token", "owner", "comment", "created"}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
structure.myLockBuilder = new Lock.Builder();
}
@Override
public void characters(String s, SvnInfoStructure structure) {
}
}
private static class LockToken extends ElementHandlerBase {
private LockToken() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.myLockBuilder.setToken(s);
}
}
private static class LockOwner extends ElementHandlerBase {
private LockOwner() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.myLockBuilder.setOwner(s);
}
}
private static class LockComment extends ElementHandlerBase {
private LockComment() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.myLockBuilder.setComment(s);
}
}
private static class LockCreated extends ElementHandlerBase {
private LockCreated() {
super(new String[]{}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
structure.myLockBuilder.setCreationDate(SvnUtil.parseDate(s));
}
}
private static class Entry extends ElementHandlerBase {
@Nullable private final File myBase;
private Entry(@Nullable final File base) {
super(new String[]{"url", "relative-url", "lock", "repository","wc-info","commit","tree-conflict"}, new String[]{"conflict"});
myBase = base;
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException {
final String kind = attributes.getValue("kind");
structure.myKind = NodeKind.from(notNullize(kind));
if (myBase != null) {
final String path = attributes.getValue("path");
assertSAX(!StringUtil.isEmptyOrSpaces(path));
structure.myFile = SvnUtil.resolvePath(myBase, path);
}
final String revision = attributes.getValue("revision");
assertSAX(! StringUtil.isEmptyOrSpaces(revision));
try {
structure.myRevision = Long.parseLong(revision);
} catch (NumberFormatException e) {
structure.myRevision = -1;
//throw new SAXException(e);
}
}
@Override
public void characters(String s, SvnInfoStructure structure) {
}
}
private static class Info extends ElementHandlerBase {
private Info() {
super(new String[]{}, new String[]{"entry"});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
}
}
private static class Fake extends ElementHandlerBase {
private Fake() {
super(new String[]{"info"}, new String[]{});
}
@Override
protected void updateInfo(Attributes attributes, SvnInfoStructure structure) {
}
@Override
public void characters(String s, SvnInfoStructure structure) {
}
}
private abstract static class ElementHandlerBase {
private final Set<String> myAwaitedChildren;
private final Set<String> myAwaitedChildrenMultiple;
private ElementHandlerBase parent;
ElementHandlerBase(String[] awaitedChildren, String[] awaitedChildrenMultiple) {
myAwaitedChildren = new HashSet<>(Arrays.asList(awaitedChildren));
myAwaitedChildrenMultiple = new HashSet<>(Arrays.asList(awaitedChildrenMultiple));
}
@NotNull
public ElementHandlerBase getParent() {
return parent;
}
public void setParent(@NotNull ElementHandlerBase parent) {
this.parent = parent;
}
protected abstract void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException;
public boolean startElement(String uri, String localName, String qName, Attributes attributes) {
if (myAwaitedChildrenMultiple.contains(qName)) {
return true;
}
return myAwaitedChildren.remove(qName);
}
public abstract void characters(String s, SvnInfoStructure structure) throws SAXException;
}
}
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.idea.svn.info;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.api.Depth;
import org.jetbrains.idea.svn.api.NodeKind;
import org.jetbrains.idea.svn.api.Revision;
import org.jetbrains.idea.svn.api.Url;
import org.jetbrains.idea.svn.checkin.CommitInfo;
import org.jetbrains.idea.svn.commandLine.SvnBindException;
import org.jetbrains.idea.svn.conflict.ConflictAction;
import org.jetbrains.idea.svn.conflict.ConflictOperation;
import org.jetbrains.idea.svn.conflict.ConflictReason;
import org.jetbrains.idea.svn.lock.Lock;
import org.xml.sax.SAXException;
import java.io.File;
import java.util.Date;
import static com.intellij.openapi.util.text.StringUtil.notNullize;
import static com.intellij.util.ObjectUtils.doIfNotNull;
import static org.jetbrains.idea.svn.SvnUtil.createUrl;
public class SvnInfoStructure {
@Nullable public File myFile;
public String relativeUrl;
public Url myUrl;
public Url myRootURL;
public long myRevision;
public NodeKind myKind;
public String myUuid;
public CommitInfo.Builder myCommitInfoBuilder;
public String mySchedule;
public Url myCopyFromURL;
public long myCopyFromRevision;
public String myTextTime;
public String myPropTime;
public String myChecksum;
public String myConflictOld;
public String myConflictNew;
public String myConflictWorking;
public String myPropRejectFile;
public Lock.Builder myLockBuilder;
public Depth myDepth;
public String myChangelistName;
public long myWcSize;
public Date myCorrectCommittedDate;
public Date myCorrectTextDate;
public TreeConflictDescription myTreeConflict;
public Info convert() throws SAXException, SvnBindException {
return new Info(myFile, myUrl, Revision.of(myRevision), myKind, myRootURL, myUuid, getCommitInfo(), mySchedule,
myDepth, myCopyFromURL, Revision.of(myCopyFromRevision), getLock(), myConflictOld, myConflictNew, myConflictWorking,
createTreeConflict());
}
@Nullable
private Lock getLock() {
return myLockBuilder != null ? myLockBuilder.build() : null;
}
@Nullable
private CommitInfo getCommitInfo() {
return doIfNotNull(myCommitInfoBuilder, CommitInfo.Builder::build);
}
private org.jetbrains.idea.svn.conflict.TreeConflictDescription createTreeConflict() throws SAXException, SvnBindException {
if (myTreeConflict == null) {
return null;
}
else {
assert myFile != null;
return new org.jetbrains.idea.svn.conflict.TreeConflictDescription(myFile, myKind, ConflictAction.from(myTreeConflict.myAction),
ConflictReason.from(myTreeConflict.myReason),
ConflictOperation.from(myTreeConflict.myOperation),
createVersion(myTreeConflict.mySourceLeft),
createVersion(myTreeConflict.mySourceRight));
}
}
private org.jetbrains.idea.svn.conflict.ConflictVersion createVersion(final ConflictVersion version)
throws SvnBindException, SAXException {
return version == null
? null
: new org.jetbrains.idea.svn.conflict.ConflictVersion(createUrl(version.myRepoUrl), version.myPathInRepo,
parseRevision(version.myRevision),
NodeKind.from(notNullize(version.myKind)));
}
private long parseRevision(final String revision) throws SAXException {
try {
return Long.parseLong(revision);
} catch (NumberFormatException e) {
throw new SAXException(e);
}
}
public static class TreeConflictDescription {
public String myOperation;
public String myKind;
public String myReason;
public String myVictim;
public String myAction;
public ConflictVersion mySourceLeft;
public ConflictVersion mySourceRight;
}
public static class ConflictVersion {
public String myKind;
public String myPathInRepo;
public String myRepoUrl;
public String myRevision;
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment