Skip to content

Commit

Permalink
Add a BDO copy method to IBaseDataObjectHelper.
Browse files Browse the repository at this point in the history
  • Loading branch information
James Cover jdcove2 committed Nov 8, 2024
1 parent c657040 commit fc2b8cf
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 2 deletions.
110 changes: 110 additions & 0 deletions src/main/java/emissary/core/IBaseDataObjectHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -97,6 +98,115 @@ public static IBaseDataObject clone(final IBaseDataObject iBaseDataObject, final
return bdo;
}

/**
* Shallow copies the fields from one BaseDataObject to another BaseDataObject.
*
* @param fromBdo the BaseDataObject to copy fields from.
* @param toBdo the BaseDataObject to copy fields to.
*/
public static void copy(final BaseDataObject fromBdo, final BaseDataObject toBdo) {
Validate.isTrue(fromBdo != null, "Required: fromBdo != null");
Validate.isTrue(toBdo != null, "Required: toBdo != null");

try {
setPrivateFieldValue(toBdo, "theData", null);
setPrivateFieldValue(toBdo, "seekableByteChannelFactory", null);
} catch (ReflectiveOperationException e) {
LOGGER.info("Could not set theData/seekableByteChannelFactory!", e);
}
final SeekableByteChannelFactory sbcf = fromBdo.getChannelFactory();
if (sbcf != null) {
toBdo.setChannelFactory(sbcf);
}

toBdo.replaceCurrentForm(null);
final List<String> allCurrentForms = fromBdo.getAllCurrentForms();
for (int i = 0; i < allCurrentForms.size(); i++) {
toBdo.enqueueCurrentForm(allCurrentForms.get(i));
}

toBdo.clearParameters();
toBdo.putParameters(fromBdo.getParameters());

for (String alternateViewName : toBdo.getAlternateViewNames()) {
toBdo.addAlternateView(alternateViewName, null);
}
for (final Map.Entry<String, byte[]> entry : fromBdo.getAlternateViews().entrySet()) {
toBdo.addAlternateView(entry.getKey(), entry.getValue());
}

try {
setPrivateFieldValue(toBdo, "extractedRecords", null);
} catch (ReflectiveOperationException e) {
LOGGER.info("Could not set processingError!", e);
}
final List<IBaseDataObject> extractedRecords = fromBdo.getExtractedRecords();
if (extractedRecords != null) {
toBdo.setExtractedRecords(extractedRecords);
}

try {
setPrivateFieldValue(toBdo, "theFileName", null);
setPrivateFieldValue(toBdo, "shortName", null);
} catch (ReflectiveOperationException e) {
LOGGER.info("Could not set theFileName/shortName!", e);
}
final String filename = fromBdo.getFilename();
if (filename != null) {
toBdo.setFilename(filename);
}

try {
setPrivateFieldValue(toBdo, "procError", null);
} catch (ReflectiveOperationException e) {
LOGGER.info("Could not set processingError!", e);
}
final String processingError = fromBdo.getProcessingError();
if (processingError != null) {
toBdo.addProcessingError(processingError.substring(0, processingError.length() - 1));
}

toBdo.setCreationTimestamp(fromBdo.getCreationTimestamp());
toBdo.setPriority(fromBdo.getPriority());
toBdo.setHistory(fromBdo.getTransformHistory());
toBdo.setFontEncoding(fromBdo.getFontEncoding());
toBdo.setNumChildren(fromBdo.getNumChildren());
toBdo.setNumSiblings(fromBdo.getNumSiblings());
toBdo.setBirthOrder(fromBdo.getBirthOrder());
toBdo.setHeader(fromBdo.header() == null ? null : fromBdo.header().clone());
toBdo.setFooter(fromBdo.footer() == null ? null : fromBdo.footer().clone());
toBdo.setHeaderEncoding(fromBdo.getHeaderEncoding());
toBdo.setClassification(fromBdo.getClassification());
toBdo.setBroken(fromBdo.getBroken());
toBdo.setOutputable(fromBdo.isOutputable());
toBdo.setId(fromBdo.getId());
toBdo.setWorkBundleId(fromBdo.getWorkBundleId());
toBdo.setTransactionId(fromBdo.getTransactionId());
}

/**
* This method reflectively sets a private method that is not normally accessible. This method should only be used when
* the field must be set and there is no other way to do it. Ideally the class would be modified so that this method
* call would not be necessary.
*
* @param bdo the BaseDataObject to set the field on.
* @param fieldName the name of the field to be set.
* @param object the object that the field is to be set to.
* @throws IllegalAccessException if this {@code Field} object is enforcing Java language access control and the
* underlying field is either inaccessible or final.
* @throws NoSuchFieldException if a field with the specified name is not found.
*/
public static void setPrivateFieldValue(final BaseDataObject bdo, final String fieldName, @Nullable final Object object)
throws IllegalAccessException, NoSuchFieldException {
Validate.notNull(bdo, "Required: bdo not null");
Validate.notNull(fieldName, "Required: fieldName not null");

final Field field = bdo.getClass().getDeclaredField(fieldName);

field.setAccessible(true); // NOSONAR intentional visibility change
field.set(bdo, object); // NOSONAR intentional visibility change
}

/**
* Used to propagate needed parent information to a sprouted child. NOTE: This is taken from
* emissary.place.MultiFileServerPlace.
Expand Down
30 changes: 30 additions & 0 deletions src/test/java/emissary/core/IBaseDataObjectHelperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,36 @@ private static void checkThrowsNull(final Executable e) {
assertThrows(NullPointerException.class, e);
}

@Test
void testCopy() {
final BaseDataObject bdo = new BaseDataObject();
final BaseDataObject bdoDefault = new BaseDataObject();
final BaseDataObject bdoChanged = new BaseDataObject();
final List<String> differences = new ArrayList<>();
final DiffCheckConfiguration diffCheckConfiguration = DiffCheckConfiguration
.configure()
.enableData()
.enableTimestamp()
.enableTransformHistory()
.build();

assertThrows(IllegalArgumentException.class, () -> IBaseDataObjectHelper.copy(null, bdo));
assertThrows(IllegalArgumentException.class, () -> IBaseDataObjectHelper.copy(bdo, null));

IBaseDataObjectXmlHelperTest.setAllFieldsPrintable(bdoChanged, "Data".getBytes(StandardCharsets.UTF_8));
bdoChanged.addExtractedRecord(new BaseDataObject("ER".getBytes(StandardCharsets.UTF_8), "ER_NAME", "ER_FORM", "ER_FILETYPE"));

IBaseDataObjectHelper.copy(bdoChanged, bdo);
IBaseDataObjectDiffHelper.diff(bdoChanged, bdo, differences, diffCheckConfiguration);

assertEquals(0, differences.size(), differences.toString());

IBaseDataObjectHelper.copy(bdoDefault, bdo);
IBaseDataObjectDiffHelper.diff(bdoDefault, bdo, differences, diffCheckConfiguration);

assertEquals(0, differences.size(), differences.toString());
}

@Test
void testAddParentInformationToChild() throws Exception {
final IBaseDataObject parentIbdo = ibdo1;
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/emissary/core/IBaseDataObjectXmlHelperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ void testParentIbdoNoFieldsChanged() throws Exception {
assertNull(sha256Diff);
}

private static void setAllFieldsPrintable(final IBaseDataObject ibdo, final byte[] bytes) {
public static void setAllFieldsPrintable(final IBaseDataObject ibdo, final byte[] bytes) {
ibdo.setData(bytes);
ibdo.setBirthOrder(5);
ibdo.setBroken("Broken1");
Expand Down Expand Up @@ -88,7 +88,7 @@ private static void setAllFieldsPrintable(final IBaseDataObject ibdo, final byte
ibdo.addAlternateView("AlternateView11Key", "AlternateView11Value".getBytes(StandardCharsets.ISO_8859_1));
}

private static void setAllFieldsNonPrintable(final IBaseDataObject ibdo, final byte[] bytes) {
public static void setAllFieldsNonPrintable(final IBaseDataObject ibdo, final byte[] bytes) {
ibdo.setData(bytes);
ibdo.setBirthOrder(5);
ibdo.setBroken("\001Broken1");
Expand Down

0 comments on commit fc2b8cf

Please sign in to comment.