com.netflix.zeno.genericobject.DiffHtmlGenerator Maven / Gradle / Ivy
/*
*
* Copyright 2013 Netflix, Inc.
*
* 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 com.netflix.zeno.genericobject;
import com.netflix.zeno.genericobject.GenericObject.CollectionType;
import com.netflix.zeno.genericobject.GenericObject.Field;
import com.netflix.zeno.serializer.SerializerFactory;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* This class generates HTML describing the differences between two GenericObjects.
*
* @author dkoszewnik
*
*/
/*
* This is extremely messy code and is ripe for replacement.
*
* It is the result of an afternoon spike several months ago and has not yet needed any updates.
*/
public class DiffHtmlGenerator {
private final DiffHtmlCollectionLocker locker;
private final GenericObjectSerializationFramework genericObjectFramework;
private final boolean moreAtFromLevels[] = new boolean[1024];
private final boolean moreAtToLevels[] = new boolean[1024];
private int hierarchyLevel = 1;
/**
* Instantiate with the SerializerFactory describing an object model.
*/
public DiffHtmlGenerator(SerializerFactory factory) {
locker = new DiffHtmlCollectionLocker(factory);
genericObjectFramework = new GenericObjectSerializationFramework(factory);
}
/**
* Generate the HTML difference between two objects.
*
* @param objectType - The NFTypeSerializer name of the objects
* @param from - The first object to diff
* @param to - The second object to diff
* @return
*/
public String generateDiff(String objectType, Object from, Object to) {
GenericObject fromGenericObject = genericObjectFramework.serialize(from, objectType);
GenericObject toGenericObject = genericObjectFramework.serialize(to, objectType);
return generateDiff(fromGenericObject, toGenericObject);
}
/**
* Generate the HTML difference between two GenericObjects.
*
* @return
*/
public String generateDiff(GenericObject from, GenericObject to) {
StringBuilder builder = new StringBuilder();
builder.append("");
builder.append("");
builder.append("");
builder.append(" ");
builder.append("From ");
builder.append(" ");
builder.append("To ");
builder.append(" ");
builder.append("");
builder.append("");
writeDiff(builder, from, to);
builder.append("");
builder.append("
");
return builder.toString();
}
private void writeDiff(StringBuilder builder, GenericObject from, GenericObject to) {
if(from != null && to != null) {
writeDiffBothObjectsPresent(builder, from, to);
} else if(from != null || to != null) {
writeDiffOneObjectNull(builder, from, to);
}
}
private void writeDiffBothObjectsPresent(StringBuilder builder, GenericObject from, GenericObject to) {
if(from.getCollectionType() == CollectionType.COLLECTION) {
writeCollectionDiff(builder, from, to);
} else if(from.getCollectionType() == CollectionType.MAP) {
writeMapDiff(builder, from, to);
} else {
writeObjectDiff(builder, from, to);
}
}
private void writeCollectionDiff(StringBuilder builder, GenericObject from, GenericObject to) {
locker.lockCollectionFields(from, to);
/// both objects' fields length should be the same after the lockCollectionFields operation
for(int i=0;i fieldList, int position) {
if(position >= fieldList.size())
return false;
for(int i=position+1;i");
if(fromField != null && fromField.getCollectionPosition() != 0)
builder.append("").append(fromField.getCollectionPosition()).append(" ");
else
builder.append(" ");
builder.append("");
if(fromField != null) {
if(nonNullValue instanceof GenericObject) {
openNewObject(builder, fromField.getFieldName(), ((GenericObject) nonNullValue).getObjectType(), moreAtFromLevel, true);
} else {
appendFieldValue(builder, fromField.getFieldName(), fromField.getValue(), moreAtFromLevel, true);
}
} else {
appendEmptyHeirarchyLevel(builder, moreAtFromLevels);
}
builder.append(" ");
if(toField != null && toField.getCollectionPosition() != 0)
builder.append("").append(toField.getCollectionPosition()).append(" ");
else
builder.append(" ");
builder.append("");
if(toField != null) {
if(nonNullValue instanceof GenericObject) {
openNewObject(builder, toField.getFieldName(), ((GenericObject) nonNullValue).getObjectType(), moreAtToLevel, false);
} else {
appendFieldValue(builder, toField.getFieldName(), toField.getValue(), moreAtToLevel, false);
}
} else {
appendEmptyHeirarchyLevel(builder, moreAtToLevels);
}
builder.append(" ");
builder.append("");
if(nonNullValue instanceof GenericObject) {
hierarchyLevel++;
writeDiff(builder, (GenericObject)fieldValue(fromField), (GenericObject)fieldValue(toField));
hierarchyLevel--;
}
moreAtFromLevels[hierarchyLevel] = false;
moreAtToLevels[hierarchyLevel] = false;
}
private Object fieldValue(Field field) {
if(field == null)
return null;
return field.getValue();
}
private Object getNonNullValue(Field fromField, Field toField) {
if(fromField != null && fromField.getValue() != null)
return fromField.getValue();
if(toField != null)
return toField.getValue();
return null;
}
private void openNewObject(StringBuilder builder, String fieldName, String typeName, boolean lastFieldAtLevel, boolean from) {
appendHierarchyLevel(builder, true, hierarchyLevel, lastFieldAtLevel, from);
builder.append(fieldName).append(": ").append("(").append(typeName).append(")\n");
}
private String cssClass(Field field1, Field field2, String missingCssClass) {
if(field1 == null)
return "empty";
if(field2 == null)
return missingCssClass;
if(field1.getValue() == null && field2.getValue() != null)
return "replace";
if(field1.getValue() == null && field2.getValue() == null)
return "equal";
if(field1.getValue() != null && field2.getValue() == null)
return "replace";
if(!(field1.getValue() instanceof GenericObject)) {
if(!field1.getValue().equals(field2.getValue()))
return "replace";
}
return "equal";
}
private void appendFieldValue(StringBuilder builder, String fieldName, Object value, boolean moreFieldsAtLevel, boolean from) {
appendHierarchyLevel(builder, false, hierarchyLevel, moreFieldsAtLevel, from);
builder.append(fieldName).append(": ").append(value).append("\n");
}
private void appendHierarchyLevel(StringBuilder builder, boolean objectField, int hierarchyLevel, boolean moreFieldsAtLevel, boolean from) {
boolean levelGuide[] = from ? moreAtFromLevels : moreAtToLevels;
for(int i=1;i");
else
builder.append(".┕━┯━>");
} else {
if(moreFieldsAtLevel)
builder.append(".├───>");
else
builder.append(".└───>");
}
}
private void appendEmptyHeirarchyLevel(StringBuilder builder, boolean[] levelGuide) {
for(int i=1;i<=hierarchyLevel;i++) {
if(levelGuide[i]) {
builder.append(" │");
} else {
for(int j=i;j<=hierarchyLevel;j++) {
if(levelGuide[j]) {
builder.append(" ");
break;
}
}
}
}
}
private static final Comparator mapFieldComparator = new Comparator() {
@SuppressWarnings({ "unchecked", "rawtypes" })
public int compare(Field o1, Field o2) {
if(o1 == null && o2 == null)
return 0;
if(o1 == null)
return 1;
if(o2 == null)
return -1;
Object key1 = getKey(o1);
Object key2 = getKey(o2);
if(key1 instanceof Comparable) {
return ((Comparable) key1).compareTo(key2);
}
return key1.hashCode() - key2.hashCode();
}
};
private static Object getKey(Field entryField) {
GenericObject entryObject = (GenericObject) entryField.getValue();
Field keyField = entryObject.getFields().get(0);
GenericObject keyObject = (GenericObject)keyField.getValue();
return keyObject.getActualObject();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy