Project

General

Profile

1
package eu.dnetlib.data.transform;
2

    
3
import java.util.List;
4

    
5
import eu.dnetlib.pace.model.FieldDef;
6
import org.apache.commons.lang.StringUtils;
7

    
8
import com.google.common.base.Splitter;
9
import com.google.common.collect.Iterables;
10
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
import com.google.protobuf.Message;
15
import com.googlecode.protobuf.format.JsonFormat;
16

    
17
import eu.dnetlib.pace.config.Type;
18

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

    
26
	private static final String COND_WRAPPER = "\\{|\\}";
27
	private static final String COND_SEPARATOR = "#";
28
	/** 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
	protected List<Object> processMultiPath(final GeneratedMessage proto, final FieldDef fieldDef, final List<String> paths) {
41
		final List<Object> response = Lists.newArrayList();
42
		for (final String pathElements : paths) {
43
			response.addAll(processPath(proto, fieldDef, pathElements));
44
		}
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
	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
	}
60

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

    
72
		final List<Object> response = Lists.newArrayList();
73

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

    
76
		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
			if (fd.isRepeated()) {
84
				int fieldCount = proto.getRepeatedFieldCount(fd);
85
				final int count = fieldDef.getSize() < 0 ? fieldCount : fieldDef.getSize() < fieldCount ? fieldDef.getSize() : fieldCount;
86

    
87
				for (int i = 0; i < count; i++) {
88
					final Object field = proto.getRepeatedField(fd, i);
89
					response.addAll(generateFields(fd, field, fieldDef, pathElements, cond));
90
				}
91
			} else {
92
				final Object field = proto.getField(fd);
93
				response.addAll(generateFields(fd, field, fieldDef, pathElements, cond));
94
			}
95
		} else throw new IllegalArgumentException("Invalid protobuf path (field not found): " + StringUtils.join(pathElements, ">") + "\nMessage:\n" + proto);
96

    
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
	private List<Object> generateFields(final FieldDescriptor fd, final Object field, final FieldDef fieldDef, final List<String> list, final String cond) {
112

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

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

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

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

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

    
136
				switch (fd.getType()) {
137
				case ENUM:
138
					res.add(((EnumValueDescriptor) field).getName());
139
					break;
140
				default:
141
					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
					break;
147
				}
148
				return res;
149
			}
150
			else throw new RuntimeException("Found a primitive type before the path end");
151
		}
152
	}
153

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