Skip to content

Commit

Permalink
Enh 37092522 - [36976776->24.09] Add OOTB Collection Extractor (merge…
Browse files Browse the repository at this point in the history
… ce/main -> ce/24.09 @ 111529)

[git-p4: depot-paths = "//dev/coherence-ce/release/coherence-ce-v24.09/": change = 111530]
  • Loading branch information
ghillert committed Sep 24, 2024
1 parent 3814001 commit 6da22c2
Show file tree
Hide file tree
Showing 8 changed files with 666 additions and 6 deletions.
55 changes: 55 additions & 0 deletions prj/coherence-core/src/main/java/com/tangosol/util/Extractors.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.ChainedFragmentExtractor;
import com.tangosol.util.extractor.CollectionExtractor;
import com.tangosol.util.extractor.FragmentExtractor;
import com.tangosol.util.extractor.IdentityExtractor;
import com.tangosol.util.extractor.KeyExtractor;
Expand All @@ -38,6 +39,7 @@
* of {@code Extractor} classes.
*
* @author lh, hr, as, mf 2018.06.14
* @author Gunnar Hillert 2024.09.19
*/
@SuppressWarnings("rawtypes")
public class Extractors
Expand Down Expand Up @@ -247,6 +249,59 @@ public static <T, E> ValueExtractor<T, E> identityCast()
return IdentityExtractor.INSTANCE;
}

/**
* Returns a {@link CollectionExtractor} that extracts the specified fields
* where extraction occurs in a chain where the result of each
* field extraction is the input to the next extractor. The result
* returned is the result of the final extractor in the chain.
*
* @param fields the field names to extract (if any field name contains a dot '.'
* that field name is split into multiple field names delimiting on
* the dots.
*
* @param <T> the type of the object to extract from
* @param <E> the type of the extracted value
*
* @return a {@link CollectionExtractor} that extracts the value(s) of the specified field(s)
*
* @throws IllegalArgumentException if the fields parameter is {@code null} or an
* empty array
*
* @see CollectionExtractor
* @see ChainedExtractor
*/
public static <T, E> CollectionExtractor<T, E> fromCollection(String... fields)
{
return new CollectionExtractor(chained(fields));
}

/**
* Returns a {@link CollectionExtractor} that wraps the specified {@link ValueExtractor}s.
* <p>
* If the {@code extractors} parameter is a single {@link ValueExtractor} then a
* {@link CollectionExtractor} is returned wrapping that extractor. If the {@code extractors} is
* multiple {@link ValueExtractor} instances in a chain, a {@link CollectionExtractor} is returned
* that wraps a {@link ChainedExtractor} that wraps the chain of {@link ValueExtractor}
* instances
*
* @param extractors the chain of {@link ValueExtractor}s to use to extract the value
* @param <T> the type of the object to extract from
* @param <E> the type of the extracted value
*
* @return a {@link CollectionExtractor} that extracts the value(s) of the specified field(s)
*
* @throws IllegalArgumentException if the fields parameter is {@code null} or an
* empty array
*
* @see CollectionExtractor
* @see ChainedExtractor
*
*/
public static <T, E> CollectionExtractor<T, E> fromCollection(ValueExtractor<?, ?>... extractors)
{
return new CollectionExtractor(chained(extractors));
}

/**
* Returns an extractor that extracts the value of the specified index(es)
* from a POF encoded binary value.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* https://oss.oracle.com/licenses/upl.
*/
package com.tangosol.util.extractor;

import com.tangosol.io.ExternalizableLite;
import com.tangosol.io.pof.PofReader;
import com.tangosol.io.pof.PofWriter;
import com.tangosol.io.pof.PortableObject;

import com.tangosol.util.ValueExtractor;

import jakarta.json.bind.annotation.JsonbCreator;
import jakarta.json.bind.annotation.JsonbProperty;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* Collection Extractor is used to extract values from Collections using the provided {@link ValueExtractor}.
* Important Note:
*
* <ul>
* <li>If the {@link ValueExtractor} is null, an {@link IllegalStateException} is raised.
* <li>If the provided {@link Collection} is null or empty, an empty {@link List} is returned.
* </ul>
*
* @author Gunnar Hillert 2024.08.28
* @param <T> – the type of the value to extract from
* @param <E> - the type of value that will be extracted
*/
public class CollectionExtractor<T, E>
extends AbstractExtractor<Collection<T>, List<E>>
implements ExternalizableLite, PortableObject
{

// ----- constructors ---------------------------------------------------

/**
* Default constructor (necessary for the {@link ExternalizableLite} interface).
*/
public CollectionExtractor()
{
}

/**
* Construct a CollectionExtractor based on the specified {@link ValueExtractor}.
*
* @param extractor the ValueExtractor
*/
public CollectionExtractor(ValueExtractor<T, E> extractor)
{
if (extractor == null)
{
throw new IllegalArgumentException("The CollectionExtractor requires a ValueExtractor to be specified");
}
m_extractor = extractor;
}

/**
* Construct a CollectionExtractor based on the specified {@link ValueExtractor}.
*
* @param extractor the ValueExtractor
* @param nTarget one of the {@link #VALUE} or {@link #KEY} values
*/
@JsonbCreator
public CollectionExtractor(@JsonbProperty("extractor")
ValueExtractor<T, E> extractor,
@JsonbProperty("target")
int nTarget)
{
this(extractor);

azzert(nTarget == VALUE || nTarget == KEY, String.format(
"nTarget must be either %s or %s", VALUE, KEY));
m_nTarget = nTarget;
}

// ----- CollectionExtractor methods ---------------------------------------

/**
* Extract the value from the passed {@link Collection} using the underlying extractor.
* If the {@link ValueExtractor} is null, an {@link IllegalStateException} is raised.
* If the provided {@link Collection} is null or empty, an empty {@link List} is returned.
*/
@Override
public List<E> extract(Collection<T> target)
{
if (m_extractor == null)
{
throw new IllegalStateException("The CollectionExtractor requires a ValueExtractor to be specified");
}
List<E> results = new ArrayList<>();
if (target == null || target.isEmpty())
{
return results;
}

for (T res : target)
{
results.add(m_extractor.extract(res));
}
return results;
}

@Override
public ValueExtractor<Collection<T>, List<E>> fromKey()
{
return new CollectionExtractor<T, E>(m_extractor, KEY);
}

// ----- CanonicallyNamed interface -------------------------------------

/**
* Compute a canonical name.
*
* @return canonical name.
*/
@Override
public String getCanonicalName()
{
if (m_sNameCanon == null)
{
m_sNameCanon = m_extractor.getCanonicalName();
}
return m_sNameCanon;
}

// ----- ExternalizableLite interface -----------------------------------



@Override
public void readExternal(DataInput in)
throws IOException
{
m_extractor = readObject(in);
m_nTarget = readInt(in);
}

@Override
public void writeExternal(DataOutput out)
throws IOException
{
writeObject(out, m_extractor);
writeInt(out, m_nTarget);
}


// ----- PortableObject interface ---------------------------------------

@Override
public void readExternal(PofReader in)
throws IOException
{
m_extractor = in.readObject(0);
m_nTarget = in.readInt(1);
}

@Override
public void writeExternal(PofWriter out)
throws IOException
{
out.writeObject(0, m_extractor);
out.writeInt(1, m_nTarget);
}

// ----- data fields ----------------------------------------------------

/**
* The underlying ValueExtractor.
*/
@JsonbProperty("extractor")
protected ValueExtractor<T, E> m_extractor;

}
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,11 @@ descriptor: pof-config.xsd.
<class-name>com.tangosol.util.UniversalManipulator</class-name>
</user-type>

<user-type>
<type-id>198</type-id>
<class-name>com.tangosol.util.extractor.CollectionExtractor</class-name>
</user-type>

<!-- com.tangosol.util.filter package (continued) (200-209) -->

<user-type>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* https://oss.oracle.com/licenses/upl.
*/
package data.collectionExtractor;

import com.tangosol.io.ExternalizableLite;

import com.tangosol.io.pof.PofReader;
import com.tangosol.io.pof.PofWriter;
import com.tangosol.io.pof.PortableObject;

import java.io.IOException;
import java.io.Serializable;

import java.util.Objects;

/**
* A class representing a City.
*
* @author Gunnar Hillert 2024.09.10
*/
public class City implements Serializable, PortableObject, ExternalizableLite
{
private String name;
private int population;

public City()
{
}

public City(String name, int population)
{
this.name = name;
this.population = population;
}

public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}

public int getPopulation()
{
return population;
}

public void setPopulation(int population)
{
this.population = population;
}

@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (!(o instanceof City city)) return false;
return population == city.population && Objects.equals(name, city.name);
}

@Override
public int hashCode()
{
return Objects.hash(name, population);
}

public String toString()
{
return "Country{" +
"name='" + name + '\'' +
", population=" + population +
'}';
}

public void readExternal(PofReader in) throws IOException
{
name = in.readString(0);
population = in.readInt(1);
}

public void writeExternal(PofWriter out) throws IOException
{
out.writeString(0, name);
out.writeInt( 1, population);
}
}
Loading

0 comments on commit 6da22c2

Please sign in to comment.