/*
* Copyright 2016 the original author or authors.
*
* 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 org.gradle.api.internal.artifacts.ivyservice.modulecache;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.SetMultimap;
import org.gradle.api.artifacts.ModuleVersionIdentifier;
import org.gradle.api.artifacts.ModuleVersionSelector;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector;
import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory;
import org.gradle.api.internal.artifacts.ivyservice.NamespaceId;
import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions;
import org.gradle.internal.component.external.descriptor.Artifact;
import org.gradle.internal.component.external.descriptor.Configuration;
import org.gradle.internal.component.external.descriptor.DefaultExclude;
import org.gradle.internal.component.external.descriptor.MavenScope;
import org.gradle.internal.component.external.descriptor.ModuleDescriptorState;
import org.gradle.internal.component.external.descriptor.MutableModuleDescriptorState;
import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
import org.gradle.internal.component.external.model.DefaultMutableIvyModuleResolveMetadata;
import org.gradle.internal.component.external.model.DefaultMutableMavenModuleResolveMetadata;
import org.gradle.internal.component.external.model.IvyDependencyMetadata;
import org.gradle.internal.component.external.model.IvyModuleResolveMetadata;
import org.gradle.internal.component.external.model.MavenDependencyMetadata;
import org.gradle.internal.component.external.model.MavenModuleResolveMetadata;
import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetadata;
import org.gradle.internal.component.model.DefaultIvyArtifactName;
import org.gradle.internal.component.model.DependencyMetadata;
import org.gradle.internal.component.model.Exclude;
import org.gradle.internal.component.model.IvyArtifactName;
import org.gradle.internal.serialize.Decoder;
import org.gradle.internal.serialize.Encoder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ModuleMetadataSerializer {
private static final byte TYPE_IVY = 1;
private static final byte TYPE_MAVEN = 2;
public MutableModuleComponentResolveMetadata read(Decoder decoder, ImmutableModuleIdentifierFactory moduleIdentifierFactory, ModuleExclusions moduleExclusions) throws IOException {
return new Reader(decoder, moduleIdentifierFactory).read();
}
public void write(Encoder encoder, ModuleComponentResolveMetadata metadata) throws IOException {
new Writer(encoder).write(metadata);
}
private static class Writer {
private final Encoder encoder;
private Writer(Encoder encoder) {
this.encoder = encoder;
}
public void write(ModuleComponentResolveMetadata metadata) throws IOException {
if (metadata instanceof IvyModuleResolveMetadata) {
write((IvyModuleResolveMetadata) metadata);
} else if (metadata instanceof MavenModuleResolveMetadata) {
write((MavenModuleResolveMetadata) metadata);
} else {
throw new IllegalArgumentException("Unexpected metadata type: " + metadata.getClass());
}
}
private void write(MavenModuleResolveMetadata metadata) throws IOException {
encoder.writeByte(TYPE_MAVEN);
writeInfoSection(metadata);
writeDependencies(metadata.getDependencies());
writeSharedInfo(metadata);
writeNullableString(metadata.getSnapshotTimestamp());
writeNullableString(metadata.getPackaging());
writeBoolean(metadata.isRelocated());
}
private void write(IvyModuleResolveMetadata metadata) throws IOException {
encoder.writeByte(TYPE_IVY);
writeInfoSection(metadata);
writeConfigurations(metadata.getConfigurationDefinitions().values());
writeDependencies(metadata.getDependencies());
writeSharedInfo(metadata);
}
private void writeSharedInfo(ModuleComponentResolveMetadata metadata) throws IOException {
ModuleDescriptorState md = metadata.getDescriptor();
writeArtifacts(md.getArtifacts());
writeExcludeRules(md.getExcludes());
}
private void writeId(ModuleComponentIdentifier componentIdentifier) throws IOException {
writeString(componentIdentifier.getGroup());
writeString(componentIdentifier.getModule());
writeString(componentIdentifier.getVersion());
}
private void writeInfoSection(ModuleComponentResolveMetadata metadata) throws IOException {
writeId(metadata.getComponentId());
ModuleDescriptorState md = metadata.getDescriptor();
ModuleComponentIdentifier componentIdentifier = md.getComponentIdentifier();
writeId(componentIdentifier);
writeString(md.getStatus());
writeBoolean(md.isGenerated());
writeNullableString(md.getDescription());
writeNullableDate(md.getPublicationDate());
writeNullableString(md.getBranch());
writeExtraInfo(md.getExtraInfo());
}
private void writeExtraInfo(Map<NamespaceId, String> extraInfo) throws IOException {
writeCount(extraInfo.size());
for (Map.Entry<NamespaceId, String> entry : extraInfo.entrySet()) {
NamespaceId namespaceId = entry.getKey();
writeString(namespaceId.getNamespace());
writeString(namespaceId.getName());
writeString(entry.getValue());
}
}
private void writeConfigurations(Collection<Configuration> configurations) throws IOException {
writeCount(configurations.size());
for (Configuration conf : configurations) {
writeConfiguration(conf);
}
}
private void writeConfiguration(Configuration conf) throws IOException {
writeString(conf.getName());
writeBoolean(conf.isTransitive());
writeBoolean(conf.isVisible());
writeStringList(conf.getExtendsFrom());
}
private void writeArtifacts(List<Artifact> artifacts) throws IOException {
writeCount(artifacts.size());
for (Artifact artifact : artifacts) {
IvyArtifactName artifactName = artifact.getArtifactName();
writeString(artifactName.getName());
writeString(artifactName.getType());
writeNullableString(artifactName.getExtension());
writeNullableString(artifactName.getClassifier());
writeStringSet(artifact.getConfigurations());
}
}
private void writeDependencies(List<? extends DependencyMetadata> dependencies) throws IOException {
writeCount(dependencies.size());
for (DependencyMetadata dd : dependencies) {
writeDependency(dd);
}
}
private void writeDependency(DependencyMetadata dep) throws IOException {
ModuleVersionSelector selector = dep.getRequested();
writeString(selector.getGroup());
writeString(selector.getName());
writeString(selector.getVersion());
if (dep instanceof IvyDependencyMetadata) {
IvyDependencyMetadata ivyDependency = (IvyDependencyMetadata) dep;
encoder.writeByte(TYPE_IVY);
writeDependencyConfigurationMapping(ivyDependency);
writeArtifacts(ivyDependency.getDependencyArtifacts());
writeExcludeRules(ivyDependency.getExcludes());
writeString(ivyDependency.getDynamicConstraintVersion());
writeBoolean(ivyDependency.isForce());
writeBoolean(ivyDependency.isChanging());
writeBoolean(ivyDependency.isTransitive());
} else if (dep instanceof MavenDependencyMetadata) {
MavenDependencyMetadata mavenDependency = (MavenDependencyMetadata) dep;
encoder.writeByte(TYPE_MAVEN);
writeArtifacts(mavenDependency.getDependencyArtifacts());
writeExcludeRules(mavenDependency.getExcludes());
encoder.writeSmallInt(mavenDependency.getScope().ordinal());
encoder.writeBoolean(mavenDependency.isOptional());
} else {
throw new IllegalStateException("Unexpected dependency type");
}
}
private void writeDependencyConfigurationMapping(IvyDependencyMetadata dep) throws IOException {
SetMultimap<String, String> confMappings = dep.getConfMappings();
writeCount(confMappings.keySet().size());
for (String conf : confMappings.keySet()) {
writeString(conf);
writeStringSet(confMappings.get(conf));
}
}
private void writeExcludeRules(List<Exclude> excludes) throws IOException {
writeCount(excludes.size());
for (Exclude exclude : excludes) {
IvyArtifactName artifact = exclude.getArtifact();
writeString(exclude.getModuleId().getGroup());
writeString(exclude.getModuleId().getName());
writeString(artifact.getName());
writeString(artifact.getType());
writeString(artifact.getExtension());
writeStringArray(exclude.getConfigurations().toArray(new String[0]));
writeString(exclude.getMatcher());
}
}
private void writeCount(int i) throws IOException {
encoder.writeSmallInt(i);
}
private void writeString(String str) throws IOException {
encoder.writeString(str);
}
private void writeNullableString(String str) throws IOException {
encoder.writeNullableString(str);
}
private void writeBoolean(boolean b) throws IOException {
encoder.writeBoolean(b);
}
private void writeNullableDate(Date publicationDate) throws IOException {
if (publicationDate == null) {
writeLong(-1);
} else {
writeLong(publicationDate.getTime());
}
}
private void writeLong(long l) throws IOException {
encoder.writeLong(l);
}
private void writeStringArray(String[] values) throws IOException {
writeCount(values.length);
for (String configuration : values) {
writeNullableString(configuration);
}
}
private void writeStringList(List<String> values) throws IOException {
writeCount(values.size());
for (String configuration : values) {
writeString(configuration);
}
}
private void writeStringSet(Set<String> values) throws IOException {
writeCount(values.size());
for (String configuration : values) {
writeString(configuration);
}
}
}
private static class Reader {
private final Decoder decoder;
private final ImmutableModuleIdentifierFactory moduleIdentifierFactory;
private MutableModuleDescriptorState md;
private ModuleComponentIdentifier id;
private ModuleVersionIdentifier mvi;
private Reader(Decoder decoder, ImmutableModuleIdentifierFactory moduleIdentifierFactory) {
this.decoder = decoder;
this.moduleIdentifierFactory = moduleIdentifierFactory;
}
public MutableModuleComponentResolveMetadata read() throws IOException {
byte type = decoder.readByte();
switch (type) {
case TYPE_IVY:
return readIvy();
case TYPE_MAVEN:
return readMaven();
default:
throw new IllegalArgumentException("Unexpected metadata type found.");
}
}
private void readSharedInfo() throws IOException {
readArtifacts();
readAllExcludes();
}
private MutableModuleComponentResolveMetadata readMaven() throws IOException {
readInfoSection();
List<DependencyMetadata> dependencies = readDependencies();
readSharedInfo();
String snapshotTimestamp = readNullableString();
String packaging = readNullableString();
boolean relocated = readBoolean();
DefaultMutableMavenModuleResolveMetadata metadata = new DefaultMutableMavenModuleResolveMetadata(mvi, id, md, packaging, relocated, dependencies);
metadata.setSnapshotTimestamp(snapshotTimestamp);
return metadata;
}
private MutableModuleComponentResolveMetadata readIvy() throws IOException {
readInfoSection();
List<Configuration> configurations = readConfigurations();
List<DependencyMetadata> dependencies = readDependencies();
readSharedInfo();
return new DefaultMutableIvyModuleResolveMetadata(mvi, id, md, configurations, dependencies);
}
private void readInfoSection() throws IOException {
id = readId();
ModuleComponentIdentifier componentIdentifier = readId();
String status = readString();
boolean generated = readBoolean();
md = new MutableModuleDescriptorState(componentIdentifier, status, generated);
md.setDescription(readNullableString());
md.setPublicationDate(readNullableDate());
md.setBranch(readNullableString());
mvi = moduleIdentifierFactory.moduleWithVersion(componentIdentifier.getGroup(), componentIdentifier.getModule(), componentIdentifier.getVersion());
readExtraInfo();
}
private ModuleComponentIdentifier readId() throws IOException {
return DefaultModuleComponentIdentifier.newId(readString(), readString(), readString());
}
private void readExtraInfo() throws IOException {
int len = readCount();
for (int i = 0; i < len; i++) {
NamespaceId namespaceId = new NamespaceId(readString(), readString());
String value = readString();
md.getExtraInfo().put(namespaceId, value);
}
}
private List<Configuration> readConfigurations() throws IOException {
int len = readCount();
List<Configuration> configurations = new ArrayList<Configuration>(len);
for (int i = 0; i < len; i++) {
Configuration configuration = readConfiguration();
configurations.add(configuration);
}
return configurations;
}
private Configuration readConfiguration() throws IOException {
String name = readString();
boolean transitive = readBoolean();
boolean visible = readBoolean();
List<String> extendsFrom = readStringList();
return new Configuration(name, transitive, visible, extendsFrom);
}
private void readArtifacts() throws IOException {
int size = readCount();
for (int i = 0; i < size; i++) {
IvyArtifactName ivyArtifactName = new DefaultIvyArtifactName(readString(), readString(), readNullableString(), readNullableString());
md.addArtifact(ivyArtifactName, readStringSet());
}
}
private List<DependencyMetadata> readDependencies() throws IOException {
int len = readCount();
List<DependencyMetadata> result = Lists.newArrayListWithCapacity(len);
for (int i = 0; i < len; i++) {
result.add(readDependency());
}
return result;
}
private DependencyMetadata readDependency() throws IOException {
ModuleVersionSelector requested = DefaultModuleVersionSelector.newSelector(readString(), readString(), readString());
byte type = decoder.readByte();
switch (type) {
case TYPE_IVY:
SetMultimap<String, String> configMappings = readDependencyConfigurationMapping();
List<Artifact> artifacts = readDependencyArtifactDescriptors();
List<Exclude> excludes = readExcludeRules();
String dynamicConstraintVersion = readString();
boolean force = readBoolean();
boolean changing = readBoolean();
boolean transitive = readBoolean();
return new IvyDependencyMetadata(requested, dynamicConstraintVersion, force, changing, transitive, configMappings, artifacts, excludes);
case TYPE_MAVEN:
artifacts = readDependencyArtifactDescriptors();
excludes = readExcludeRules();
MavenScope scope = MavenScope.values()[decoder.readSmallInt()];
boolean optional = decoder.readBoolean();
return new MavenDependencyMetadata(scope, optional, requested, artifacts, excludes);
default:
throw new IllegalArgumentException("Unexpected dependency type found.");
}
}
private SetMultimap<String, String> readDependencyConfigurationMapping() throws IOException {
int size = readCount();
SetMultimap<String, String> result = LinkedHashMultimap.create();
for (int i = 0; i < size; i++) {
String from = readString();
Set<String> to = readStringSet();
result.putAll(from, to);
}
return result;
}
private List<Artifact> readDependencyArtifactDescriptors() throws IOException {
int size = readCount();
List<Artifact> result = Lists.newArrayListWithCapacity(size);
for (int i = 0; i < size; i++) {
IvyArtifactName ivyArtifactName = new DefaultIvyArtifactName(readString(), readString(), readNullableString(), readNullableString());
result.add(new Artifact(ivyArtifactName, readStringSet()));
}
return result;
}
private List<Exclude> readExcludeRules() throws IOException {
int len = readCount();
List<Exclude> result = Lists.newArrayListWithCapacity(len);
for (int i = 0; i < len; i++) {
DefaultExclude rule = readExcludeRule();
result.add(rule);
}
return result;
}
private DefaultExclude readExcludeRule() throws IOException {
String moduleOrg = readString();
String moduleName = readString();
String artifact = readString();
String type = readString();
String ext = readString();
String[] confs = readStringArray();
String matcher = readString();
return new DefaultExclude(moduleIdentifierFactory.module(moduleOrg, moduleName), artifact, type, ext, confs, matcher);
}
private void readAllExcludes() throws IOException {
int len = readCount();
for (int i = 0; i < len; i++) {
md.addExclude(readExcludeRule());
}
}
private int readCount() throws IOException {
return decoder.readSmallInt();
}
private String readString() throws IOException {
return decoder.readString();
}
private String readNullableString() throws IOException {
return decoder.readNullableString();
}
private boolean readBoolean() throws IOException {
return decoder.readBoolean();
}
private Date readNullableDate() throws IOException {
long value = readLong();
if (value == -1) {
return null;
} else {
return new Date(value);
}
}
private long readLong() throws IOException {
return decoder.readLong();
}
private String[] readStringArray() throws IOException {
int size = readCount();
String[] array = new String[size];
for (int i = 0; i < size; i++) {
array[i] = readNullableString();
}
return array;
}
private List<String> readStringList() throws IOException {
int size = readCount();
List<String> list = new ArrayList<String>(size);
for (int i = 0; i < size; i++) {
list.add(readString());
}
return list;
}
private Set<String> readStringSet() throws IOException {
int size = readCount();
Set<String> set = new LinkedHashSet<String>(size);
for (int i = 0; i < size; i++) {
set.add(readString());
}
return set;
}
}
}