blob: 1f5fb4b39ef7ccdba479b875f12ccecc1880626b [file] [log] [blame]
// Copyright (C) 2015 The Android Open Source Project
//
// 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.
package com.googlesource.gerrit.plugins.repositoryuse;
import org.apache.commons.digester3.Digester;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class ManifestParser {
private static final Logger log =
LoggerFactory.getLogger(ManifestParser.class);
private HashMap<String, String> remotes;
private Project defaultProject;
private ArrayList<Project> projects;
public ManifestParser() {
remotes = new HashMap<>();
defaultProject = new Project(null, null, null);
projects = new ArrayList<>();
}
public Map<String, String> parseManifest(byte[] contents) {
Digester digester = new Digester();
digester.push(this);
// Add all remote handlers
digester.addCallMethod("manifest/remote", "addRemote", 2);
digester.addCallParam("manifest/remote", 0, "name");
digester.addCallParam("manifest/remote", 1, "fetch");
// Add all default handlers. This handles both repo format
// attributes (remote, revision) and non-standard attributes
// (branch, tag, commit-id).
digester.addCallMethod("manifest/default", "addDefault", 5);
digester.addCallParam("manifest/default", 0, "remote");
digester.addCallParam("manifest/default", 1, "revision");
digester.addCallParam("manifest/default", 2, "branch");
digester.addCallParam("manifest/default", 3, "tag");
digester.addCallParam("manifest/default", 4, "commit-id");
// Add all project handlers. This handles both repo format
// attributes (remote, revision) and non-standard attributes
// (branch, tag, commit-id).
digester.addCallMethod("manifest/project", "addProject", 6);
digester.addCallParam("manifest/project", 0, "remote");
digester.addCallParam("manifest/project", 1, "name");
digester.addCallParam("manifest/project", 2, "revision");
digester.addCallParam("manifest/project", 3, "branch");
digester.addCallParam("manifest/project", 4, "tag");
digester.addCallParam("manifest/project", 5, "commit-id");
InputStream input = new ByteArrayInputStream(contents);
try {
digester.parse(input);
} catch (IOException | SAXException e) {
log.warn("Unable to parse manifest", e);
}
HashMap<String, String> resolvedProjects = new HashMap<>(projects.size());
for (Project p : projects) {
String uri = null;
String revision = null;
if (p.getRemote() != null) {
uri = remotes.get(p.getRemote());
}
if (uri == null && defaultProject.getRemote() != null) {
uri = remotes.get(defaultProject.getRemote());
}
if (uri == null && p.getRemote() != null) {
uri = p.getRemote();
}
if (uri != null) {
uri += "/" + p.getName();
}
if (p.getRevision() != null) {
revision = p.getRevision();
} else if (defaultProject.getRevision() != null) {
revision = defaultProject.getRevision();
}
if (uri != null && revision != null) {
resolvedProjects.put(uri, revision);
} else {
log.warn("Invalid project description in manifest");
}
}
return resolvedProjects;
}
public void addRemote(String name, String fetch) {
if (fetch != null && fetch.endsWith("/")) {
fetch = fetch.substring(0, fetch.length()-1);
}
remotes.put(name, fetch);
}
public void addDefault(String remote, String revision, String branch,
String tag, String commitId) {
defaultProject =
new Project(remote, null, getRevision(revision, branch, tag, commitId));
}
public void addProject(String remote, String name, String revision,
String branch, String tag, String commitId) {
if (name != null) {
projects.add(new Project(remote, name,
getRevision(revision, branch, tag, commitId)));
} else {
log.warn("Project name not specified in manifest");
}
}
private String getRevision(String revision, String branch, String tag,
String commitId) {
if (revision != null) {
return revision;
} else if (branch != null) {
return branch;
} else if (tag != null) {
return tag;
} else if (commitId != null) {
return commitId;
}
return null;
}
public static class Project {
private String remote;
private String name;
private String revision;
public Project(String remote, String name, String revision) {
this.remote = remote;
this.name = name;
this.revision = revision;
if (this.name != null && this.name.endsWith("/")) {
this.name = this.name.substring(0, this.name.length()-1);
}
if (this.name != null && this.name.endsWith(".git")) {
this.name = this.name.substring(0, this.name.length()-4);
}
if (this.name != null && this.name.startsWith("/")) {
this.name = this.name.substring(1);
}
}
public String getRemote() {
return remote;
}
public String getName() {
return name;
}
public String getRevision() {
return revision;
}
}
}