Java tutorial
// Copyright (C) 2017 Google Inc // // 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.supermanifest; import static com.google.gerrit.reviewdb.client.RefNames.REFS_HEADS; import com.google.gerrit.reviewdb.client.Project; import com.googlesource.gerrit.plugins.supermanifest.SuperManifestRefUpdatedListener.GerritRemoteReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.Queue; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.transform.stream.StreamSource; import org.apache.commons.lang.StringUtils; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.lib.Repository; class JiriManifestParser { static class ManifestItem { public ManifestItem(String repoKey, String manifest, String ref, String pKey, boolean revisionPinned) { this.repoKey = repoKey; this.manifest = manifest; this.ref = ref; this.revisionPinned = revisionPinned; this.projectKey = pKey; } String repoKey; String manifest; String ref; boolean revisionPinned; // In jiri if import is pinned to a revision and if // we have a corresponding project in the manifest, jiri would // pin that project to same revision. So passing key to match // project to import tag. // For Eg, if you have manifest in manifest2 repo // <manifest><projects><project name="manifest2" .../> // And If you import this from your main manifest // <manifest><imports><import name="manifest2" revision="A"... /> // jiri will pin manifest2 project to A as well. String projectKey; } static class RepoMap<K, V extends Repository> extends HashMap<K, V> implements AutoCloseable { @Override public void close() { for (Repository repo : this.values()) { repo.close(); } } } public static JiriProjects getProjects(GerritRemoteReader reader, String repoKey, String ref, String manifest) throws ConfigInvalidException, IOException { try (RepoMap<String, Repository> repoMap = new RepoMap<>()) { repoMap.put(repoKey, reader.openRepository(repoKey)); Queue<ManifestItem> q = new LinkedList<>(); q.add(new ManifestItem(repoKey, manifest, ref, "", false)); HashMap<String, HashSet<String>> processedRepoFiles = new HashMap<>(); HashMap<String, JiriProjects.Project> projectMap = new HashMap<>(); while (q.size() != 0) { ManifestItem mi = q.remove(); Repository repo = repoMap.get(mi.repoKey); if (repo == null) { repo = reader.openRepository(mi.repoKey); repoMap.put(mi.repoKey, repo); } HashSet<String> processedFiles = processedRepoFiles.get(mi.repoKey); if (processedFiles == null) { processedFiles = new HashSet<String>(); processedRepoFiles.put(mi.repoKey, processedFiles); } if (processedFiles.contains(mi.manifest)) { continue; } processedFiles.add(mi.manifest); JiriManifest m; try { m = parseManifest(repo, mi.ref, mi.manifest); } catch (JAXBException | XMLStreamException e) { throw new ConfigInvalidException("XML parse error", e); } for (JiriProjects.Project project : m.projects.getProjects()) { project.fillDefault(); if (mi.revisionPinned && project.Key().equals(mi.projectKey)) { project.setRevision(mi.ref); } if (projectMap.containsKey(project.Key())) { if (!projectMap.get(project.Key()).equals(project)) throw new ConfigInvalidException(String.format( "Duplicate conflicting project %s in manifest %s\n%s\n%s", project.Key(), mi.manifest, project.toString(), projectMap.get(project.Key()).toString())); } else { projectMap.put(project.Key(), project); } } URI parentURI; try { parentURI = new URI(mi.manifest); } catch (URISyntaxException e) { throw new ConfigInvalidException("Invalid parent URI", e); } for (JiriManifest.LocalImport l : m.imports.getLocalImports()) { ManifestItem tw = new ManifestItem(mi.repoKey, parentURI.resolve(l.getFile()).getPath(), mi.ref, mi.projectKey, mi.revisionPinned); q.add(tw); } for (JiriManifest.Import i : m.imports.getImports()) { i.fillDefault(); URI uri; try { uri = new URI(i.getRemote()); } catch (URISyntaxException e) { throw new ConfigInvalidException("Invalid URI", e); } String iRepoKey = new Project.NameKey(StringUtils.strip(uri.getPath(), "/")).toString(); String iRef = i.getRevision(); boolean revisionPinned = true; if (iRef.isEmpty()) { iRef = REFS_HEADS + i.getRemotebranch(); revisionPinned = false; } ManifestItem tmi = new ManifestItem(iRepoKey, i.getManifest(), iRef, i.Key(), revisionPinned); q.add(tmi); } } return new JiriProjects(projectMap.values().toArray(new JiriProjects.Project[0])); } } private static JiriManifest parseManifest(Repository repo, String ref, String file) throws JAXBException, IOException, XMLStreamException { byte[] b = Utils.readBlob(repo, ref + ":" + file); JAXBContext jc = JAXBContext.newInstance(JiriManifest.class); XMLInputFactory inf = XMLInputFactory.newFactory(); inf.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); inf.setProperty(XMLInputFactory.SUPPORT_DTD, false); XMLStreamReader sr = inf.createXMLStreamReader(new StreamSource(new ByteArrayInputStream(b))); return (JiriManifest) jc.createUnmarshaller().unmarshal(sr); } }