Project

General

Profile

1 35948 claudio.at
package eu.dnetlib.data.transform;
2
3
import java.util.List;
4
5 54253 claudio.at
import eu.dnetlib.pace.model.FieldDef;
6 35948 claudio.at
import org.apache.commons.lang.StringUtils;
7
8
import com.google.common.base.Splitter;
9 37507 claudio.at
import com.google.common.collect.Iterables;
10 35948 claudio.at
import com.google.common.collect.Lists;
11
import com.google.protobuf.Descriptors.EnumValueDescriptor;
12
import com.google.protobuf.Descriptors.FieldDescriptor;
13
import com.google.protobuf.GeneratedMessage;
14 38166 claudio.at
import com.google.protobuf.Message;
15
import com.googlecode.protobuf.format.JsonFormat;
16 35948 claudio.at
17 38166 claudio.at
import eu.dnetlib.pace.config.Type;
18
19 35948 claudio.at
/**
20
 * AbstractProtoMapper provide common navigation methods on the protocolbuffers Messages.
21
 *
22
 * @author claudio
23
 */
24
public abstract class AbstractProtoMapper {
25
26 37507 claudio.at
	private static final String COND_WRAPPER = "\\{|\\}";
27
	private static final String COND_SEPARATOR = "#";
28 35948 claudio.at
	/** The Constant PATH_SEPARATOR. */
29
	private static final String PATH_SEPARATOR = "/";
30
31
	/**
32
	 * Process multi path.
33
	 *
34
	 * @param proto
35
	 *            the proto
36
	 * @param paths
37
	 *            the paths
38
	 * @return the list
39
	 */
40 54253 claudio.at
	protected List<Object> processMultiPath(final GeneratedMessage proto, final FieldDef fieldDef, final List<String> paths) {
41 35948 claudio.at
		final List<Object> response = Lists.newArrayList();
42
		for (final String pathElements : paths) {
43 54253 claudio.at
			response.addAll(processPath(proto, fieldDef, pathElements));
44 35948 claudio.at
		}
45
		return response;
46
	}
47
48
	/**
49
	 * Process path.
50
	 *
51
	 * @param proto
52
	 *            the proto
53
	 * @param path
54
	 *            the path
55
	 * @return the list
56
	 */
57 54253 claudio.at
	protected List<Object> processPath(final GeneratedMessage proto, final FieldDef fieldDef, final String path) {
58
		return processPath(proto, fieldDef, Lists.newLinkedList(Splitter.on(PATH_SEPARATOR).trimResults().split(path)));
59 35948 claudio.at
	}
60
61
	/**
62
	 * Process path.
63
	 *
64
	 * @param proto
65
	 *            the proto
66
	 * @param pathElements
67
	 *            the list
68
	 * @return the list
69
	 */
70 54253 claudio.at
	protected List<Object> processPath(final GeneratedMessage proto, final FieldDef fieldDef, final List<String> pathElements) {
71 35948 claudio.at
72
		final List<Object> response = Lists.newArrayList();
73
74
		if (pathElements.isEmpty()) throw new RuntimeException("ProtoBuf navigation path is empty");
75
76 37507 claudio.at
		final String fieldPathCond = pathElements.get(0);
77
78
		final String fieldPath = StringUtils.substringBefore(fieldPathCond, "[");
79
		final String cond = getCondition(fieldPathCond);
80
81
		final FieldDescriptor fd = proto.getDescriptorForType().findFieldByName(fieldPath);
82
		if ((fd != null)) {
83 35948 claudio.at
			if (fd.isRepeated()) {
84 54253 claudio.at
				int fieldCount = proto.getRepeatedFieldCount(fd);
85
				final int count = fieldDef.getSize() < 0 ? fieldCount : fieldDef.getSize() < fieldCount ? fieldDef.getSize() : fieldCount;
86
87 35948 claudio.at
				for (int i = 0; i < count; i++) {
88
					final Object field = proto.getRepeatedField(fd, i);
89 54253 claudio.at
					response.addAll(generateFields(fd, field, fieldDef, pathElements, cond));
90 35948 claudio.at
				}
91
			} else {
92
				final Object field = proto.getField(fd);
93 54253 claudio.at
				response.addAll(generateFields(fd, field, fieldDef, pathElements, cond));
94 35948 claudio.at
			}
95 37507 claudio.at
		} else throw new IllegalArgumentException("Invalid protobuf path (field not found): " + StringUtils.join(pathElements, ">") + "\nMessage:\n" + proto);
96 35948 claudio.at
97
		return response;
98
	}
99
100
	/**
101
	 * Generate fields.
102
	 *
103
	 * @param fd
104
	 *            the fd
105
	 * @param field
106
	 *            the field
107
	 * @param list
108
	 *            the list
109
	 * @return the list
110
	 */
111 54253 claudio.at
	private List<Object> generateFields(final FieldDescriptor fd, final Object field, final FieldDef fieldDef, final List<String> list, final String cond) {
112 37507 claudio.at
113
		final List<Object> res = Lists.newArrayList();
114 35948 claudio.at
		if (field instanceof GeneratedMessage) {
115 37507 claudio.at
			if (list.size() > 1) {
116
117 54253 claudio.at
				if (StringUtils.isBlank(cond)) return processPath((GeneratedMessage) field, fieldDef, list.subList(1, list.size()));
118 37507 claudio.at
				else {
119
120
					final List<String> condPath =
121
							Lists.newLinkedList(Splitter.on(COND_SEPARATOR).trimResults().split(StringUtils.substringBefore(cond, "=")));
122
123 54253 claudio.at
					final String val = (String) Iterables.getOnlyElement(processPath((GeneratedMessage) field, fieldDef, condPath));
124 37507 claudio.at
					final String condVal = StringUtils.substringAfter(cond, "=").replaceAll(COND_WRAPPER, "").trim();
125
126 54253 claudio.at
					return val.equals(condVal) ? processPath((GeneratedMessage) field, fieldDef, list.subList(1, list.size())) : res;
127 37507 claudio.at
				}
128
			}
129 54253 claudio.at
			else if (Type.JSON.equals(fieldDef.getType())) {
130 38166 claudio.at
				res.add(JsonFormat.printToString((Message) field));
131
				return res;
132
			} else throw new RuntimeException("No primitive type found");
133 35948 claudio.at
		} else {
134
			if (list.size() == 1) {
135 37507 claudio.at
136 35948 claudio.at
				switch (fd.getType()) {
137
				case ENUM:
138
					res.add(((EnumValueDescriptor) field).getName());
139
					break;
140
				default:
141 54253 claudio.at
					if (field instanceof String && fieldDef.getLength() > 0) {
142
						res.add(StringUtils.substring((String) field, 0, fieldDef.getLength()));
143
					} else {
144
						res.add(field);
145
					}
146 35948 claudio.at
					break;
147
				}
148
				return res;
149
			}
150
			else throw new RuntimeException("Found a primitive type before the path end");
151
		}
152
	}
153 37507 claudio.at
154
	private String getCondition(final String fieldPathCond) {
155
		return fieldPathCond.contains("[") ? StringUtils.substringAfter(fieldPathCond, "[").replace("]", "") : "";
156
	}
157 35948 claudio.at
}