Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HOP-4269, HOP-4449, HOP-4285, HOP-4281 #1734

Merged
merged 6 commits into from
Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions core/src/main/java/org/apache/hop/core/gui/Point.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,16 @@

package org.apache.hop.core.gui;

import org.apache.hop.metadata.api.HopMetadataProperty;

import java.util.Objects;

public class Point {

public Point() {
this(0,0);
}

public Point(int x, int y) {
this.x = x;
this.y = y;
Expand All @@ -30,7 +37,10 @@ public Point(Point p) {
this.y = p.y;
}

@HopMetadataProperty(key = "xloc")
public int x;

@HopMetadataProperty(key = "yloc")
public int y;

public void multiply(float factor) {
Expand All @@ -54,4 +64,40 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hash(x, y);
}

/**
* Gets x
*
* @return value of x
*/
public int getX() {
return x;
}

/**
* Sets x
*
* @param x value of x
*/
public void setX(int x) {
this.x = x;
}

/**
* Gets y
*
* @return value of y
*/
public int getY() {
return y;
}

/**
* Sets y
*
* @param y value of y
*/
public void setY(int y) {
this.y = y;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@

Class<? extends IHopMetadataObjectFactory> objectFactory() default
HopMetadataDefaultObjectFactory.class;

String xmlKey() default "";
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,9 @@
*/
Class<? extends InjectionTypeConverter> injectionConverter() default
DefaultInjectionTypeConverter.class;

/**
* @return true to store metadata inline with the parent metadata, not in a sub-element.
*/
boolean inline() default false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.hop.metadata.serializer.xml;

import org.apache.hop.core.Const;
import org.apache.hop.metadata.api.HopMetadataProperty;

import java.lang.reflect.Field;
import java.util.function.Function;

public class MetadataPropertyKeyFunction implements Function<Field, String> {
@Override
public String apply(Field field) {
HopMetadataProperty annotation = field.getAnnotation(HopMetadataProperty.class);
if (annotation == null) {
return null;
}
return Const.NVL(annotation.groupKey(), Const.NVL(annotation.key(), field.getName()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,15 @@
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.exception.HopXmlException;
import org.apache.hop.core.xml.XmlHandler;
import org.apache.hop.metadata.api.HopMetadataProperty;
import org.apache.hop.metadata.api.IEnumHasCode;
import org.apache.hop.metadata.api.IHopMetadata;
import org.apache.hop.metadata.api.IHopMetadataProvider;
import org.apache.hop.metadata.api.*;
import org.apache.hop.metadata.util.ReflectionUtil;
import org.w3c.dom.Node;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.function.Function;

public class XmlMetadataUtil {
/**
Expand All @@ -46,17 +44,15 @@ public class XmlMetadataUtil {
* @throws HopException
*/
public static String serializeObjectToXml(Object object) throws HopException {
String xml = "";
Class<?> objectClass = object.getClass();

// Pick up all the @HopMetadataProperty annotations
// Serialize them to XML
//
List<Field> fields = new ArrayList(ReflectionUtil.findAllFields(object.getClass()));
String xml = "";

// Sort the fields by name to get stable XML as output
// Pick up all the fields with @HopMetadataProperty annotation, sorted by name.
// Serialize them to XML.
//
Collections.sort(fields, Comparator.comparing(Field::getName));

List<Field> fields =
ReflectionUtil.findAllFields(objectClass, new MetadataPropertyKeyFunction());
for (Field field : fields) {
// Don't serialize fields flagged as transient or volatile
//
Expand Down Expand Up @@ -94,7 +90,7 @@ public static String serializeObjectToXml(Object object) throws HopException {
if (property.storeWithName()) {
xml += XmlHandler.addTagValue(tag, ((IHopMetadata) value).getName());
} else {
xml += serializeObjectToXml(value, groupKey, tag, isPassword, storeWithCode);
xml += serializeObjectToXml(property, value, groupKey, tag, isPassword, storeWithCode);
}
}
}
Expand All @@ -104,7 +100,12 @@ public static String serializeObjectToXml(Object object) throws HopException {
}

private static String serializeObjectToXml(
Object value, String groupKey, String tag, boolean password, boolean storeWithCode)
HopMetadataProperty property,
Object value,
String groupKey,
String tag,
boolean password,
boolean storeWithCode)
throws HopException {

String xml = "";
Expand Down Expand Up @@ -149,7 +150,7 @@ private static String serializeObjectToXml(
//
List listItems = (List) value;
for (Object listItem : listItems) {
xml += serializeObjectToXml(listItem, groupKey, tag, password, storeWithCode);
xml += serializeObjectToXml(property, listItem, groupKey, tag, password, storeWithCode);
}

if (StringUtils.isNotEmpty(groupKey)) {
Expand All @@ -162,9 +163,13 @@ private static String serializeObjectToXml(
// We only take the fields of the POJO class that are annotated
// We wrap the POJO properties in the provided tag
//
xml += XmlHandler.openTag(tag) + Const.CR;
if (!property.inline()) {
xml += XmlHandler.openTag(tag) + Const.CR;
}
xml += serializeObjectToXml(value);
xml += XmlHandler.closeTag(tag) + Const.CR;
if (!property.inline()) {
xml += XmlHandler.closeTag(tag) + Const.CR;
}
}
}
return xml;
Expand All @@ -182,8 +187,29 @@ private static String serializeObjectToXml(
public static <T> T deSerializeFromXml(
Node node, Class<? extends T> clazz, IHopMetadataProvider metadataProvider)
throws HopXmlException {
return deSerializeFromXml(node, clazz, null, metadataProvider);
return deSerializeFromXml(null, node, clazz, null, metadataProvider);
}

/**
* Load the metadata in the provided XML node and return it as a new object. It does this by
* looking at the HopMetadataProperty annotations of the fields in the object's class.
*
* @param parentObject An optional parent object to allow a factory to load extra information
* from.
* @param node The metadata to read
* @param clazz the class to de-serialize
* @param metadataProvider to load name references from
* @throws HopXmlException
*/
public static <T> T deSerializeFromXml(
Object parentObject,
Node node,
Class<? extends T> clazz,
IHopMetadataProvider metadataProvider)
throws HopXmlException {
return deSerializeFromXml(parentObject, node, clazz, null, metadataProvider);
}

/**
* Load the metadata in the provided XML node into the given object. It does this by looking at
* the HopMetadataProperty annotations of the fields in the object's class.
Expand All @@ -197,9 +223,55 @@ public static <T> T deSerializeFromXml(
public static <T> T deSerializeFromXml(
Node node, Class<? extends T> clazz, T object, IHopMetadataProvider metadataProvider)
throws HopXmlException {
return deSerializeFromXml(null, node, clazz, object, metadataProvider);
}

/**
* Load the metadata in the provided XML node into the given object. It does this by looking at
* the HopMetadataProperty annotations of the fields in the object's class.
*
* @param parentObject An optional parent object to allow factories to retrieve extra information.
* @param node The metadata to read
* @param clazz the class to de-serialize
* @param object The object to load into. If null: create a new object.
* @param metadataProvider to load name references from
* @throws HopXmlException
*/
public static <T> T deSerializeFromXml(
Object parentObject,
Node node,
Class<? extends T> clazz,
T object,
IHopMetadataProvider metadataProvider)
throws HopXmlException {
if (object == null) {
try {
object = clazz.getDeclaredConstructor().newInstance();

// See if this is an interface where we need to use a factory.
//
HopMetadataObject metadataObject = clazz.getAnnotation(HopMetadataObject.class);
if (metadataObject != null) {
String xmlKey = metadataObject.xmlKey();
if (StringUtils.isEmpty(xmlKey)) {
throw new HopXmlException(
"Please specify which XML attribute to consider the key. Hop will use this key to create the appropriate class instance of type "
+ clazz);
}
String objectId = XmlHandler.getNodeValue(XmlHandler.getSubNode(node, xmlKey));
if (StringUtils.isEmpty(objectId)) {
throw new HopXmlException(
"XML attribute "
+ xmlKey
+ " is needed to instantiate type "
+ clazz
+ " but it wasn't provided");
}
IHopMetadataObjectFactory factory =
metadataObject.objectFactory().getConstructor().newInstance();
object = (T) factory.createObject(objectId, parentObject);
} else {
object = clazz.getDeclaredConstructor().newInstance();
}
} catch (Exception e) {
throw new HopXmlException(
"Unable to create a new instance of class "
Expand All @@ -209,10 +281,11 @@ public static <T> T deSerializeFromXml(
}
}

// Pick up all the @HopMetadataProperty annotations
// Serialize them to XML
// Pick up all the @HopMetadataProperty annotations.
// The fields are sorted by name to get a stable XML output when serialized.
//
Set<Field> fields = ReflectionUtil.findAllFields(clazz);
List<Field> fields =
ReflectionUtil.findAllFields(object.getClass(), new MetadataPropertyKeyFunction());
for (Field field : fields) {
// Don't serialize fields flagged as transient or volatile
//
Expand All @@ -233,7 +306,12 @@ public static <T> T deSerializeFromXml(
boolean password = property.password();
boolean storeWithCode = property.storeWithCode();

Node tagNode = XmlHandler.getSubNode(node, tag);
Node tagNode;
if (property.inline()) {
tagNode = node;
} else {
tagNode = XmlHandler.getSubNode(node, tag);
}
Node groupNode;
if (StringUtils.isEmpty(groupKey)) {
groupNode = node;
Expand All @@ -242,6 +320,7 @@ public static <T> T deSerializeFromXml(
}
Object value =
deSerializeFromXml(
object,
fieldType,
groupNode,
tagNode,
Expand Down Expand Up @@ -276,6 +355,7 @@ public static <T> T deSerializeFromXml(
}

private static Object deSerializeFromXml(
Object parentObject,
Class<?> fieldType,
Node groupNode,
Node elementNode,
Expand All @@ -287,7 +367,6 @@ private static Object deSerializeFromXml(
boolean password,
boolean storeWithCode)
throws HopXmlException {

String elementString = XmlHandler.getNodeValue(elementNode);

if (storeWithName) {
Expand Down Expand Up @@ -372,6 +451,7 @@ private static Object deSerializeFromXml(
try {
Object newItem =
deSerializeFromXml(
parentObject,
listClass,
null,
itemNode,
Expand Down Expand Up @@ -401,7 +481,7 @@ private static Object deSerializeFromXml(
} else {
// Load the metadata for this node...
//
return deSerializeFromXml(elementNode, fieldType, metadataProvider);
return deSerializeFromXml(parentObject, elementNode, fieldType, metadataProvider);
}

// No value found for the given arguments: return the default value
Expand Down
Loading