
org.basinmc.plunger.mapping.mcp.InnerClassMapping Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2018 Johannes Donath
* and other copyright owners as documented in the project's IP log.
*
* 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.basinmc.plunger.mapping.mcp;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author Johannes Donath
*/
public final class InnerClassMapping {
private final Map mappings;
private final Map innerClasses;
@JsonCreator
private InnerClassMapping(@NonNull Map mappings) {
this.mappings = mappings;
this.innerClasses = mappings.entrySet().stream()
.filter((e) -> e.getKey().contains("$"))
.filter((e) -> !e.getValue().getInnerClasses().isEmpty())
.flatMap((e) -> e.getValue().getInnerClasses().stream()
.map((cl) -> new SimpleImmutableEntry<>(e.getKey(), cl)))
.filter((e) -> e.getKey().equals(e.getValue().getInnerName()))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
}
/**
* Decodes a nested class mapping from the supplied file.
*
* @param path a file path.
* @return a decoded class map.
* @throws IOException when reading or decoding fails.
*/
@NonNull
public static InnerClassMapping read(@NonNull Path path) throws IOException {
try (InputStream inputStream = Files.newInputStream(path)) {
return read(inputStream);
}
}
/**
* Decodes a nested class mapping from the supplied input stream.
*
* @param inputStream an input stream.
* @return a decoded class map.
* @throws IOException when reading or decoding fails.
*/
@NonNull
public static InnerClassMapping read(@NonNull InputStream inputStream) throws IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(inputStream, InnerClassMapping.class);
}
/**
* Retrieves an inner class mapping from within this map.
*
* @param className a class name.
* @return a mapping or an empty optional.
*/
@NonNull
public Optional getMapping(@NonNull String className) {
return Optional.ofNullable(this.mappings.get(className));
}
/**
* Retrieves the declaration of an inner class based on its name.
*
* @param className a class name.
* @return an inner class or an empty optional.
*/
@NonNull
public Optional getInnerClass(@NonNull String className) {
return Optional.ofNullable(this.innerClasses.get(className));
}
/**
* Represents an owner type (e.g. the parent of a type) as well as its method of origin (if any).
*/
public static final class EnclosingMethod {
private final String descriptor;
private final String name;
private final String owner;
@JsonCreator
private EnclosingMethod(
@NonNull @JsonProperty(value = "owner", required = true) String owner,
@Nullable @JsonProperty("name") String name,
@Nullable @JsonProperty("desc") String descriptor) {
this.owner = owner;
this.name = name;
this.descriptor = descriptor;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || this.getClass() != o.getClass()) {
return false;
}
EnclosingMethod that = (EnclosingMethod) o;
return Objects.equals(this.owner, that.owner) &&
Objects.equals(this.name, that.name) &&
Objects.equals(this.descriptor, that.descriptor);
}
public String getDescriptor() {
return this.descriptor;
}
public String getName() {
return this.name;
}
public String getOwner() {
return this.owner;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hash(this.owner, this.name, this.descriptor);
}
}
/**
* Represents a nested class (or the type itself if the class is an inner class itself and will
* thus refer to itself in Bytecode).
*/
public static class InnerClass {
private final int access;
private final String innerClass;
private final String innerName;
private final String outerClass;
private final long start;
@JsonCreator
public InnerClass(
@JsonProperty("access") String access,
@NonNull @JsonProperty(value = "inner_class", required = true) String innerClass,
@Nullable @JsonProperty("inner_name") String innerName,
@Nullable @JsonProperty("outer_class") String outerClass,
@JsonProperty("start") long start) {
this.access = access == null ? 0 : Integer.parseInt(access, 16);
this.innerClass = innerClass;
this.innerName = innerName;
this.outerClass = outerClass;
this.start = start;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || this.getClass() != o.getClass()) {
return false;
}
InnerClass that = (InnerClass) o;
return this.access == that.access &&
this.start == that.start &&
Objects.equals(this.innerClass, that.innerClass) &&
Objects.equals(this.innerName, that.innerName) &&
Objects.equals(this.outerClass, that.outerClass);
}
public int getAccess() {
return this.access;
}
@NonNull
public String getInnerClass() {
return this.innerClass;
}
@Nullable
public String getInnerName() {
return this.innerName;
}
@Nullable
public String getOuterClass() {
return this.outerClass;
}
public long getStart() {
return this.start;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects
.hash(this.access, this.innerClass, this.innerName, this.outerClass, this.start);
}
}
/**
* Represents a mapping for a single class which identifies all of its inner classes as well as
* its respective owner and method of origin.
*/
public static final class MappingEntry {
private final EnclosingMethod enclosingMethod;
private final Set innerClasses;
private MappingEntry(
@Nullable @JsonProperty("enclosingMethod") EnclosingMethod enclosingMethod,
@NonNull @JsonProperty(value = "innerClasses", required = true) Set innerClasses) {
this.enclosingMethod = enclosingMethod;
this.innerClasses = innerClasses;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || this.getClass() != o.getClass()) {
return false;
}
MappingEntry mapping = (MappingEntry) o;
return Objects.equals(this.enclosingMethod, mapping.enclosingMethod) &&
Objects.equals(this.innerClasses, mapping.innerClasses);
}
@NonNull
public Optional getEnclosingMethod() {
return Optional.ofNullable(this.enclosingMethod);
}
@NonNull
public Set getInnerClasses() {
return Collections.unmodifiableSet(this.innerClasses);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hash(this.enclosingMethod, this.innerClasses);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy