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
This diff is collapsed.
// 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