Project

General

Profile

1
package eu.dnetlib.data.transform;
2

    
3
import java.util.List;
4
import java.util.Optional;
5

    
6
import com.google.common.base.Splitter;
7
import com.google.common.collect.Iterables;
8
import com.google.common.collect.Lists;
9
import com.google.protobuf.Descriptors.EnumValueDescriptor;
10
import com.google.protobuf.Descriptors.FieldDescriptor;
11
import com.google.protobuf.GeneratedMessage;
12
import com.google.protobuf.Message;
13
import com.googlecode.protobuf.format.JsonFormat;
14
import eu.dnetlib.pace.config.Type;
15
import org.apache.commons.lang3.StringUtils;
16

    
17
/**
18
 * AbstractProtoMapper provide common navigation methods on the protocolbuffers Messages.
19
 *
20
 * @author claudio
21
 */
22
public abstract class AbstractProtoMapper {
23

    
24
	private static final String COND_WRAPPER = "\\{|\\}";
25
	private static final String COND_SEPARATOR = "#";
26
	/** The Constant PATH_SEPARATOR. */
27
	private static final String PATH_SEPARATOR = "/";
28

    
29
	/**
30
	 * Process multi path.
31
	 *
32
	 * @param proto
33
	 *            the proto
34
	 * @param paths
35
	 *            the paths
36
	 * @return the list
37
	 */
38
	protected List<Object> processMultiPath(final GeneratedMessage proto, final List<String> paths, final Type type) {
39
		final List<Object> response = Lists.newArrayList();
40
		for (final String pathElements : paths) {
41
			response.addAll(processPath(proto, pathElements, type));
42
		}
43
		return response;
44
	}
45

    
46
	/**
47
	 * Process path.
48
	 *
49
	 * @param proto
50
	 *            the proto
51
	 * @param path
52
	 *            the path
53
	 * @return the list
54
	 */
55
	protected List<Object> processPath(final GeneratedMessage proto, final String path, final Type type) {
56
		return processPath(proto, Lists.newLinkedList(Splitter.on(PATH_SEPARATOR).trimResults().split(path)), type);
57
	}
58

    
59
	/**
60
	 * Process path.
61
	 *
62
	 * @param proto
63
	 *            the proto
64
	 * @param pathElements
65
	 *            the list
66
	 * @return the list
67
	 */
68
	protected List<Object> processPath(final GeneratedMessage proto, final List<String> pathElements, final Type type) {
69

    
70
		final List<Object> response = Lists.newArrayList();
71

    
72
		if (pathElements.isEmpty()) throw new RuntimeException("ProtoBuf navigation path is empty");
73

    
74
		final String fieldPathCond = pathElements.get(0);
75

    
76
		final String fieldPath = StringUtils.substringBefore(fieldPathCond, "[");
77
		final String cond = getCondition(fieldPathCond);
78

    
79
		final Optional<FieldDescriptor> ofd = proto.getAllFields().keySet()
80
				.stream()
81
				.filter(fdef -> fdef.getName().equalsIgnoreCase(fieldPath))
82
				.findFirst();
83

    
84
		if (ofd.isPresent()) {
85
			final FieldDescriptor fd = ofd.get();
86
			if (fd.isRepeated()) {
87
				final int count = proto.getRepeatedFieldCount(fd);
88
				for (int i = 0; i < count; i++) {
89
					final Object field = proto.getRepeatedField(fd, i);
90
					response.addAll(generateFields(fd, field, pathElements, cond, type));
91
				}
92
			} else {
93
				final Object field = proto.getField(fd);
94
				response.addAll(generateFields(fd, field, pathElements, cond, type));
95
			}
96
		} //else throw new IllegalArgumentException("Invalid protobuf path (field not found): " + StringUtils.join(pathElements, ">") + "\nMessage:\n" + proto);
97

    
98
		return response;
99
	}
100

    
101
	/**
102
	 * Generate fields.
103
	 *
104
	 * @param fd
105
	 *            the fd
106
	 * @param field
107
	 *            the field
108
	 * @param list
109
	 *            the list
110
	 * @return the list
111
	 */
112
	private List<Object> generateFields(final FieldDescriptor fd, final Object field, final List<String> list, final String cond, final Type type) {
113

    
114
		final List<Object> res = Lists.newArrayList();
115
		if (field instanceof GeneratedMessage) {
116
			if (list.size() > 1) {
117

    
118
				if (StringUtils.isBlank(cond)) return processPath((GeneratedMessage) field, list.subList(1, list.size()), type);
119
				else {
120

    
121
					final List<String> condPath =
122
							Lists.newLinkedList(Splitter.on(COND_SEPARATOR).trimResults().split(StringUtils.substringBefore(cond, "=")));
123

    
124
					final String val = (String) Iterables.getOnlyElement(processPath((GeneratedMessage) field, condPath, type));
125
					final String condVal = StringUtils.substringAfter(cond, "=").replaceAll(COND_WRAPPER, "").trim();
126

    
127
					return val.equals(condVal) ? processPath((GeneratedMessage) field, list.subList(1, list.size()), type) : res;
128
				}
129
			}
130
			else if (Type.JSON.equals(type)) {
131
				final String json = JsonFormat.printToString((Message) field);
132
				res.add(json);
133
				return res;
134
			} else throw new RuntimeException("No primitive type found");
135
		} else {
136
			if (list.size() == 1) {
137

    
138
				switch (fd.getType()) {
139
				case ENUM:
140
					res.add(((EnumValueDescriptor) field).getName());
141
					break;
142
				default:
143
					res.add(field);
144
					break;
145
				}
146
				return res;
147
			}
148
			else throw new RuntimeException("Found a primitive type before the path end");
149
		}
150
	}
151

    
152
	private String getCondition(final String fieldPathCond) {
153
		return fieldPathCond.contains("[") ? StringUtils.substringAfter(fieldPathCond, "[").replace("]", "") : "";
154
	}
155
}
(1-1/13)