xyz.rk0cc.willpub.pubspec.data.dependencies.DependenciesReferenceSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jpubspec Show documentation
Show all versions of jpubspec Show documentation
Reading and writing pubspec.yaml data and save edited record on Java
package xyz.rk0cc.willpub.pubspec.data.dependencies;
import com.google.common.base.Joiner;
import xyz.rk0cc.willpub.exceptions.pubspec.IllegalPubPackageNamingException;
import xyz.rk0cc.willpub.pubspec.data.dependencies.type.DependencyReference;
import xyz.rk0cc.willpub.pubspec.data.PubspecValueValidator;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.*;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* Contains multiple {@link DependencyReference} as a {@link Set}.
*
* It embedded {@link LinkedHashMap} to allows more convenience way to get {@link DependencyReference} which {@link Set}
* can not.
*
* @since 1.0.0
*/
public sealed abstract class DependenciesReferenceSet implements Set, Serializable, Cloneable
permits ImportedReferenceSet, OverrideReferenceSet {
private final LinkedHashMap references;
private final boolean unmodifiable;
/**
* Create new empty set.
*/
DependenciesReferenceSet() {
this.references = new LinkedHashMap<>();
this.unmodifiable = false;
}
/**
* Create new set with existed reference and allows modification or not.
*
* @param references Original references.
* @param unmodifiable Forbid any modification in this object.
*/
DependenciesReferenceSet(@Nonnull DependenciesReferenceSet references, boolean unmodifiable) {
this.references = new LinkedHashMap<>(references.references);
this.unmodifiable = unmodifiable;
}
/**
* Create new modifiable set with existed reference.
*
* @param references Original reference.
*/
DependenciesReferenceSet(@Nonnull DependenciesReferenceSet references) {
this(references, false);
}
/**
* Determine it disallow edit new {@link DependencyReference} or not.
*
* Despite it provided {@link Collections#unmodifiableSet(Set)} to mark unmodifiable. It missed all methods which
* exclusive in {@link DependenciesReferenceSet} like {@link #set(DependencyReference)} and {@link #get(String)}.
*
* @return true
if disallowed.
*/
public final boolean isUnmodifiable() {
return unmodifiable;
}
/**
* An assertion method to prevent applying modification if {@link #isUnmodifiable()} return true
.
*/
private void assertModifiable() {
if (unmodifiable) throw new UnsupportedOperationException("Unmodifiable mode enabled");
}
/**
* {@inheritDoc}
*/
@Nonnegative
@Override
public final int size() {
return references.size();
}
/**
* {@inheritDoc}
*/
@Override
public final boolean isEmpty() {
return references.isEmpty();
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("SuspiciousMethodCalls")
@Override
public final boolean contains(@Nonnull Object o) {
return o instanceof String s ? references.containsKey(s) : references.containsValue(o);
}
/**
* {@inheritDoc}
*/
@Nonnull
@Override
public final Iterator iterator() {
final DependenciesReferenceSet cIDR = clone();
return new Iterator<>() {
private int handleIdx = 0;
@Override
public boolean hasNext() {
return handleIdx < cIDR.size();
}
@Override
public DependencyReference next() {
return cIDR.references.values().toArray(new DependencyReference[]{})[handleIdx++];
}
@Override
public void remove() {
String[] kSet = references.keySet().toArray(new String[]{});
assert kSet.length == size();
references.remove(kSet[handleIdx]);
}
};
}
/**
* {@inheritDoc}
*/
@Nonnull
@Override
public final Object[] toArray() {
return toNativeSet().toArray();
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("SuspiciousToArrayCall")
@Nonnull
@Override
public final T[] toArray(@Nonnull T[] a) {
return toNativeSet().toArray(a);
}
/**
* To determine allowing incoming {@link DependencyReference} can be inserted.
*
* @param dependencyReference {@link DependencyReference} which pending to apply.
*
* @return true
if allows.
*/
abstract boolean isAllowToAdd(@Nonnull DependencyReference dependencyReference);
/**
* Mock {@link #add(DependencyReference)} operation to determine the reference can be appended.
*
* @param dependencyReference {@link DependencyReference} which pending to apply.
*
* @return true
if allows.
*/
private boolean mockAddPassed(@Nonnull DependencyReference dependencyReference) {
if (!isAllowToAdd(dependencyReference)) return false;
HashMap dummy = new HashMap<>();
dummy.put(dependencyReference.name(), dependencyReference);
return dummy.containsKey(dependencyReference.name());
}
/**
* Append the reference to this set.
*
* @param dependencyReference A {@link DependencyReference} which going to apply if no
* {@link DependencyReference#name() same reference name} is applied already.
*
* @return true
if appended.
*
* @throws UnsupportedOperationException If {@link #isUnmodifiable()} returns true
.
*/
@Override
public final boolean add(@Nonnull DependencyReference dependencyReference) {
assertModifiable();
if (references.containsKey(dependencyReference.name())) return false;
if (mockAddPassed(dependencyReference)) references.putIfAbsent(dependencyReference.name(), dependencyReference);
return references.containsKey(dependencyReference.name());
}
/**
* Set the reference to this set.
*
* @param dependencyReference A {@link DependencyReference} which going to apply or modify is
* {@link DependencyReference#name() dependencies name} existed already.
*
* @return true
if set.
*
* @throws UnsupportedOperationException If {@link #isUnmodifiable()} returns true
.
*/
public final boolean set(@Nonnull DependencyReference dependencyReference) {
assertModifiable();
if (mockAddPassed(dependencyReference)) references.put(dependencyReference.name(), dependencyReference);
return references.containsKey(dependencyReference.name());
}
/**
* Remove {@link DependencyReference} with given {@link Object}, it can be either {@link DependencyReference}
* or {@link String}.
*
* @param o An object of {@link DependencyReference} or a {@link String} of package name is going to remove on this
* list.
*
* @return true
if removed.
*
* @throws UnsupportedOperationException If {@link #isUnmodifiable()} returns true
.
*/
@Override
public final boolean remove(@Nonnull Object o) {
assertModifiable();
if (o instanceof DependencyReference drt) return references.remove(drt.name(), drt);
else if (o instanceof String s) return references.remove(s) != null;
else throw new ClassCastException("'" + o.getClass().getName() + "' can not remove reference in this set");
}
/**
* {@inheritDoc}
*/
@Override
public final boolean containsAll(@Nonnull Collection> c) {
return c.stream().allMatch(ct -> {
final Stream streamType = toNativeSet().stream();
if (ct instanceof String s)
return streamType.allMatch(drts -> drts.name().equals(s));
else
return streamType.allMatch(ct::equals);
});
}
/**
* {@inheritDoc}
*/
@Override
public final boolean addAll(@Nonnull Collection extends DependencyReference> c) {
assertModifiable();
if (c.stream().allMatch(this::isAllowToAdd)) {
c.forEach(this::add);
return true;
}
return false;
}
/**
* Set all the elements and overwrite record if existed.
*
* @param c Collection that pending to overwrite if existed.
*
* @return true
if this collection changed as a result of the call.
*/
public final boolean setAll(@Nonnull Collection extends DependencyReference> c) {
assertModifiable();
if (c.stream().allMatch(this::isAllowToAdd)) {
c.forEach(this::set);
return true;
}
return false;
}
/**
* {@inheritDoc}
*
* However, to reduce complication of retaining items, the {@link Collection} items must be {@link String} only.
*/
@Override
public final boolean retainAll(@Nonnull Collection> c) {
assertModifiable();
if (!allStringCollection(c))
throw new ClassCastException("Retain all only accept String as reference");
return removeIf(dr -> c.stream().noneMatch(rmN -> rmN.equals(dr.name())));
}
/**
* {@inheritDoc}
*
* However, to reduce complication of retaining items, the {@link Collection} items must be {@link String} only.
*/
@Override
public final boolean removeAll(@Nonnull Collection> c) {
assertModifiable();
if (!allStringCollection(c))
throw new ClassCastException("Remove all only accept String as reference");
return removeIf(dr -> c.stream().anyMatch(rmN -> rmN.equals(dr.name())));
}
/**
* {@inheritDoc}
*/
@Override
public final boolean removeIf(Predicate super DependencyReference> filter) {
assertModifiable();
boolean removed = false;
for (DependencyReference dr : this) {
if (filter.test(dr)) {
remove(dr);
removed = true;
}
}
return removed;
}
/**
* Giving a package name to find related {@link DependencyReference}.
*
* @param dependencyName Dependency's name.
*
* @return Corresponded {@link DependencyReference} with given name.
*
* @throws IllegalPubPackageNamingException If the dependency name is not a valid name.
* @throws NullPointerException If this dependency name does not define yet.
*/
@Nonnull
public final DependencyReference get(@Nonnull String dependencyName) throws IllegalPubPackageNamingException {
PubspecValueValidator.ValueAssertion.assertPackageNaming(dependencyName);
return Objects.requireNonNull(references.get(dependencyName));
}
/**
* Giving a package name and return specific type of {@link DependencyReference}.
*
* @param dependencyName Dependency's name.
* @param dependencyType {@link Class} which preferred return type of {@link DependencyReference}.
* @param Return type of {@link DependencyReference}.
*
* @return Corresponded {@link DependencyReference} with given name.
*
* @throws IllegalPubPackageNamingException If the dependency name is not a valid name.
* @throws NullPointerException If this dependency name does not define yet.
* @throws ClassCastException If actual type of {@link DependencyReference} is difference from
* dependencyType
.
*/
@SuppressWarnings("unchecked")
@Nonnull
public final D get(
@Nonnull String dependencyName,
@Nonnull Class dependencyType
) throws IllegalPubPackageNamingException {
if (Modifier.isAbstract(dependencyType.getModifiers()))
throw new IllegalArgumentException("Do not uses abstracted dependency type to apply.");
final DependencyReference initGet = get(dependencyName);
if (!initGet.getClass().equals(dependencyType))
throw new ClassCastException(
"Dependency '" + dependencyName + "' is " + initGet.getClass().getName() +
", not " + dependencyType.getName()
);
return (D) initGet;
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException If {@link #isUnmodifiable()} returns true
.
*/
@Override
public final void clear() {
assertModifiable();
references.clear();
}
/**
* {@inheritDoc}
*/
@Override
@Nonnull
public final Spliterator spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
/**
* {@inheritDoc}
*/
@Nonnull
@Override
public final Stream stream() {
return StreamSupport.stream(spliterator(), false);
}
/**
* {@inheritDoc}
*/
@Nonnull
@Override
public final Stream parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
/**
* {@inheritDoc}
*/
@Override
public final void forEach(@Nonnull Consumer super DependencyReference> action) {
for (DependencyReference dependencyReference : this) {
action.accept(dependencyReference);
}
}
/**
* Convert it to a native {@link Set} which missing {@link #set(DependencyReference)} and {@link #get(String)}
* features.
*
* @return {@link Set} with limited feature.
*/
@Nonnull
public final Set toNativeSet() {
return new LinkedHashSet<>(references.values());
}
/**
* Clone a new {@link DependenciesReferenceSet} with exact same data.
*
* @return New {@link DependenciesReferenceSet} with same data.
*/
@Override
public abstract DependenciesReferenceSet clone();
@Nonnull
@Override
public final String toString() {
return '[' + Joiner.on(",\n").join(references.values()) + ']';
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private static boolean allStringCollection(@Nonnull Collection> c) {
return c.stream().allMatch(ci -> ci instanceof String);
}
}