1 | import "source-map-support/register";
|
2 | // TODO should I get rid of the lib above for production browser build?
|
3 | import { defaultsDeep as defaultsDeepM } from "lodash";
|
4 | import { camelCase, concat, curry, fromPairs, isArray, isObject, isString, toPairsIn } from "lodash/fp";
|
5 | import * as hl from "highland";
|
6 | import * as VError from "verror";
|
7 | import * as iassign from "immutable-assign";
|
8 | iassign.setOption({
|
9 | // Deep freeze both input and output. Used in development to make sure they don't change.
|
10 | // TODO watch issue and re-enable when addressed: https://github.com/engineforce/ImassignM/issues/11
|
11 | //freeze: true,
|
12 | ignoreIfNoChange: true
|
13 | });
|
14 | import { isDefinedCXML, unionLSV } from "./gpml-utilities";
|
15 | import { GraphIdManager } from "./GraphIdManager";
|
16 | const GPML_ELEMENT_NAME_TO_KAAVIO_TYPE = {
|
17 | Anchor: "Burr",
|
18 | BiopaxRef: "Citation",
|
19 | DataNode: "SingleFreeNode",
|
20 | GraphicalLine: "Edge",
|
21 | Group: "Group",
|
22 | Interaction: "Edge",
|
23 | Label: "SingleFreeNode",
|
24 | //openControlledVocabulary: "Skip",
|
25 | //PublicationXref: "Skip",
|
26 | Shape: "SingleFreeNode",
|
27 | State: "Burr"
|
28 | };
|
29 | const VALUES_TO_SKIP = ["", null, undefined];
|
30 | // TODO update lodash/fp TS defs to use "x is ..."
|
31 | function isStringTS(x) {
|
32 | return isString(x);
|
33 | }
|
34 | function isArrayTS(x) {
|
35 | return isArray(x);
|
36 | }
|
37 | // NOTE: isPlainObject does not return true for an instance of a class
|
38 | function isRecord(x) {
|
39 | return !isArray(x) && isObject(x);
|
40 | }
|
41 | export class Processor {
|
42 | constructor(KeyMappings, KeyValueConverters, ValueMappings, ValueConverters) {
|
43 | this.output = {
|
44 | pathway: {
|
45 | // NOTE: GPML does not contain a way to express background color.
|
46 | // It's always just white.
|
47 | backgroundColor: "white",
|
48 | borderWidth: 0,
|
49 | color: "black",
|
50 | contains: [],
|
51 | drawAs: "rect",
|
52 | gpmlElementName: "Pathway",
|
53 | height: 0,
|
54 | // it appears type = {id: string} & type = {id?: string} makes id required.
|
55 | // TODO can we override that just for PathwayStarter?
|
56 | id: undefined,
|
57 | kaavioType: "Group",
|
58 | name: "New Pathway",
|
59 | // TODO what should the padding be?
|
60 | padding: 5,
|
61 | type: ["Pathway"],
|
62 | width: 0,
|
63 | x: 0,
|
64 | y: 0,
|
65 | zIndex: 0,
|
66 | // NOTE: these properties only apply contents of current element. They do not affect children.
|
67 | fontSize: 12,
|
68 | fontWeight: "bold",
|
69 | textAlign: "left",
|
70 | verticalAlign: "top"
|
71 | },
|
72 | entityMap: {}
|
73 | };
|
74 | this.graphIdManager = new GraphIdManager();
|
75 | this.graphIdsByGraphRef = {};
|
76 | this.containedGraphIdsByGroupGraphId = {};
|
77 | this.containedGraphIdsByGroupGroupId = {};
|
78 | this.promisedGraphIdByGroupId = {};
|
79 | this.groupIdToGraphIdStream = hl();
|
80 | this.promisedGPMLElementByGraphId = {};
|
81 | this.gpmlElementStream = hl();
|
82 | this.promisedPvjsonEntityLatestByGraphId = {};
|
83 | this.pvjsonEntityLatestStream = hl();
|
84 | this.graphIdToZIndex = {};
|
85 | this.ensureGraphIdExists = (gpmlElement) => {
|
86 | const { containedGraphIdsByGroupGroupId, graphIdManager, groupIdToGraphIdStream } = this;
|
87 | const { GroupId, GroupRef } = gpmlElement;
|
88 | let { GraphId } = gpmlElement;
|
89 | // TODO does this work for all elements? Are there any that we give an id that don't have one in GPML?
|
90 | // Does the schema allow the element to have a GraphId?
|
91 | if (!!GraphId) {
|
92 | // Does it actually have one?
|
93 | if (!isDefinedCXML(GraphId)) {
|
94 | // NOTE: we are making sure that elements that CAN have a GraphId
|
95 | // always DO have a GraphId. GraphIds are optional in GPML for Groups,
|
96 | // so we will add one if it's not already specified. But Pathway
|
97 | // elements never have GraphIds, so we don't add one for them.
|
98 | GraphId = gpmlElement.GraphId = graphIdManager.generateAndRecord();
|
99 | }
|
100 | else {
|
101 | graphIdManager.recordExisting(GraphId);
|
102 | }
|
103 | if (isDefinedCXML(GroupRef)) {
|
104 | containedGraphIdsByGroupGroupId[GroupRef] =
|
105 | containedGraphIdsByGroupGroupId[GroupRef] || [];
|
106 | containedGraphIdsByGroupGroupId[GroupRef].push(GraphId);
|
107 | }
|
108 | if (isDefinedCXML(GroupId)) {
|
109 | groupIdToGraphIdStream.write([GroupId, GraphId]);
|
110 | }
|
111 | }
|
112 | else {
|
113 | throw new Error("GraphId missing.");
|
114 | }
|
115 | return gpmlElement;
|
116 | };
|
117 | this.fillInGPMLPropertiesFromParent = curry((gpmlParentElement, gpmlChildElement) => {
|
118 | const { Graphics } = gpmlParentElement;
|
119 | // NOTE: this makes some assumptions about the distribution of ZOrder values in GPML
|
120 | // TODO This is what we used to do. Do we still need to do this? Or can we just sort them
|
121 | // based on whether they are burrs of each other?
|
122 | //element.zIndex = element.hasOwnProperty('zIndex') ? element.zIndex : referencedElement.zIndex + 1 / elementCount;
|
123 | const propertiesToFillIn = {
|
124 | Graphics: {
|
125 | ZOrder: Graphics.ZOrder
|
126 | }
|
127 | };
|
128 | /* TODO can we delete this?
|
129 | if (isDefinedCXML(gpmlParentElement.GroupRef)) {
|
130 | propertiesToFillIn.GroupRef = gpmlParentElement.GroupRef;
|
131 | }
|
132 | //*/
|
133 | return defaultsDeepM(gpmlChildElement, propertiesToFillIn);
|
134 | });
|
135 | this.getPvjsonEntityLatestByGraphId = graphId => {
|
136 | let promisedPvjsonEntity = this.promisedPvjsonEntityLatestByGraphId[graphId];
|
137 | if (promisedPvjsonEntity) {
|
138 | return promisedPvjsonEntity;
|
139 | }
|
140 | else {
|
141 | const { pvjsonEntityLatestStream } = this;
|
142 | // NOTE: we don't need to set the cache here, because the cache is
|
143 | // set for every item that flows through pvjsonEntityLatestStream
|
144 | promisedPvjsonEntity = new Promise(function (resolve, reject) {
|
145 | pvjsonEntityLatestStream
|
146 | .observe()
|
147 | .find(pvjsonEntity => pvjsonEntity.id === graphId)
|
148 | .errors(reject)
|
149 | .each(resolve);
|
150 | });
|
151 | return promisedPvjsonEntity;
|
152 | }
|
153 | };
|
154 | this.getGPMLElementByGraphId = GraphId => {
|
155 | let promisedGPMLElement = this.promisedGPMLElementByGraphId[GraphId];
|
156 | if (promisedGPMLElement) {
|
157 | return promisedGPMLElement;
|
158 | }
|
159 | else {
|
160 | const { gpmlElementStream } = this;
|
161 | // NOTE: we don't need to set the cache here, because the cache is
|
162 | // set for every item that flows through gpmlElementStream
|
163 | promisedGPMLElement = new Promise(function (resolve, reject) {
|
164 | gpmlElementStream
|
165 | .observe()
|
166 | .find(gpmlElement => gpmlElement.GraphId === GraphId)
|
167 | .errors(reject)
|
168 | .each(resolve);
|
169 | });
|
170 | return promisedGPMLElement;
|
171 | }
|
172 | };
|
173 | this.preprocessGPMLElement = (gpmlElement) => {
|
174 | const { ensureGraphIdExists, gpmlElementStream } = this;
|
175 | const processedGPMLElement = ensureGraphIdExists(gpmlElement);
|
176 | // NOTE: side effect
|
177 | gpmlElementStream.write(processedGPMLElement);
|
178 | return processedGPMLElement;
|
179 | };
|
180 | this.processGPMLAndPropertiesAndType = curry((gpmlElementName, gpmlElement) => {
|
181 | const { preprocessGPMLElement, processPropertiesAndType, pvjsonEntityLatestStream } = this;
|
182 | return processPropertiesAndType(gpmlElementName, preprocessGPMLElement(gpmlElement));
|
183 | });
|
184 | this.processProperties = curry((gpmlElement) => {
|
185 | const { processKV } = this;
|
186 | const entity = fromPairs(toPairsIn(gpmlElement).reduce((acc, x) => concat(acc, processKV(gpmlElement, x)), []));
|
187 | if (!!entity.rotation) {
|
188 | entity.textRotation = -1 * entity.rotation;
|
189 | }
|
190 | return entity;
|
191 | });
|
192 | this.processPropertiesAndType = curry((gpmlElementName, gpmlElement) => {
|
193 | const pvjsonEntity = this.processType(gpmlElementName, this.processProperties(gpmlElement));
|
194 | this.pvjsonEntityLatestStream.write(pvjsonEntity);
|
195 | return pvjsonEntity;
|
196 | });
|
197 | this.processType = curry((gpmlElementName, processed) => {
|
198 | const kaavioType = GPML_ELEMENT_NAME_TO_KAAVIO_TYPE[gpmlElementName];
|
199 | processed.type = unionLSV(processed.type, gpmlElementName, kaavioType);
|
200 | if (!!kaavioType) {
|
201 | processed.kaavioType = kaavioType;
|
202 | }
|
203 | processed.gpmlElementName = gpmlElementName;
|
204 | return processed;
|
205 | });
|
206 | this.setPvjsonEntity = pvjsonEntity => {
|
207 | const { graphIdToZIndex, promisedPvjsonEntityLatestByGraphId } = this;
|
208 | const { id, zIndex } = pvjsonEntity;
|
209 | graphIdToZIndex[id] = zIndex;
|
210 | promisedPvjsonEntityLatestByGraphId[id] = Promise.resolve(pvjsonEntity);
|
211 | this.output = iassign(this.output, function (o) {
|
212 | return o.entityMap;
|
213 | }, function (entityMap) {
|
214 | entityMap[pvjsonEntity.id] = pvjsonEntity;
|
215 | return entityMap;
|
216 | });
|
217 | };
|
218 | this.getGPMLKeyAsJSFunctionName = (gpmlKey) => {
|
219 | // NOTE: gpmlKeyAsJSFunctionName is for attributes like "Data-Source", because
|
220 | // the following would be invalid JS:
|
221 | // export function Data-Source() {};
|
222 | // TODO what about things like spaces, etc.?
|
223 | return gpmlKey.replace("-", "");
|
224 | };
|
225 | this.getPvjsonValue = (gpmlElement, gpmlKey, gpmlValue) => {
|
226 | const { getGPMLKeyAsJSFunctionName, getPvjsonValue, processKV, ValueConverters, ValueMappings } = this;
|
227 | const gpmlKeyAsJSFunctionName = getGPMLKeyAsJSFunctionName(gpmlKey);
|
228 | let pvjsonValue;
|
229 | try {
|
230 | if (ValueConverters.hasOwnProperty(gpmlKeyAsJSFunctionName)) {
|
231 | return ValueConverters[gpmlKeyAsJSFunctionName](gpmlElement);
|
232 | }
|
233 | else if (isStringTS(gpmlValue)) {
|
234 | if (ValueMappings.hasOwnProperty(gpmlValue)) {
|
235 | return ValueMappings[gpmlValue];
|
236 | }
|
237 | else {
|
238 | return gpmlValue;
|
239 | }
|
240 | }
|
241 | else if (isArrayTS(gpmlValue)) {
|
242 | return gpmlValue.map(valueItem => {
|
243 | return getPvjsonValue(valueItem, gpmlKey, valueItem);
|
244 | });
|
245 | }
|
246 | else if (isRecord(gpmlValue)) {
|
247 | return fromPairs(toPairsIn(gpmlValue).reduce((acc, [key, value]) => {
|
248 | processKV(gpmlValue, [key, value]).forEach(function (x) {
|
249 | acc.push(x);
|
250 | });
|
251 | return acc;
|
252 | }, []));
|
253 | }
|
254 | else {
|
255 | return gpmlValue;
|
256 | }
|
257 | }
|
258 | catch (err) {
|
259 | throw new VError(err, ` when calling
|
260 | getPvjsonValue(
|
261 | ${JSON.stringify(gpmlElement, null, "")},
|
262 | ${JSON.stringify(gpmlKey, null, "")},
|
263 | ${JSON.stringify(gpmlValue, null, "")}
|
264 | )
|
265 | `);
|
266 | }
|
267 | };
|
268 | this.processKV = curry((gpmlElement, [gpmlKey, gpmlValue]) => {
|
269 | try {
|
270 | const { getGPMLKeyAsJSFunctionName, getPvjsonValue, KeyMappings, KeyValueConverters, processKV, ValueMappings } = this;
|
271 | const gpmlKeyAsJSFunctionName = getGPMLKeyAsJSFunctionName(gpmlKey);
|
272 | if (VALUES_TO_SKIP.indexOf(gpmlValue) > -1) {
|
273 | return [];
|
274 | }
|
275 | if (KeyValueConverters.hasOwnProperty(gpmlKeyAsJSFunctionName)) {
|
276 | return KeyValueConverters[gpmlKeyAsJSFunctionName](gpmlElement, KeyMappings, ValueMappings);
|
277 | }
|
278 | const pvjsonKey = KeyMappings[gpmlKey];
|
279 | // NOTE "pvjson:merge" is for elements like "Graphics", where they
|
280 | // are nested in GPML but are merged into the parent in pvjson.
|
281 | if (gpmlKey[0] === "_" ||
|
282 | pvjsonKey === "pvjson:delete" ||
|
283 | (isObject(gpmlValue) && !isDefinedCXML(gpmlValue))) {
|
284 | // NOTE: we don't want to include "private" keys, such as
|
285 | // "_exists" or "_namespace".
|
286 | return [];
|
287 | }
|
288 | else if (pvjsonKey === "pvjson:merge") {
|
289 | return toPairsIn(gpmlValue).reduce((acc, pair) => concat(acc, processKV(gpmlElement, pair)), []);
|
290 | }
|
291 | else if (pvjsonKey === "pvjson:each") {
|
292 | // NOTE: Example of what uses this is GPML Attribute.
|
293 | // (in GPML, 'Attribute' is an XML *ELEMENT* named "Attribute")
|
294 | return toPairsIn(gpmlValue
|
295 | .filter(({ Key, Value }) => VALUES_TO_SKIP.indexOf(Value) === -1)
|
296 | .map(({ Key, Value }) => {
|
297 | return processKV(gpmlElement, [Key, Value]);
|
298 | })
|
299 | .reduce((acc, [[processedKey, processedValue]]) => {
|
300 | // NOTE: this looks more complicated than it needs to be,
|
301 | // but it's to handle the case where there are two or more
|
302 | // sibling Attribute elements that share the same Key.
|
303 | // I don't know of any cases of this in our actual GPML,
|
304 | // but the XSD does not require unique Keys for sibling
|
305 | // Attributes.
|
306 | if (acc.hasOwnProperty(processedKey)) {
|
307 | acc[processedKey] = unionLSV(acc[processedKey], processedValue);
|
308 | }
|
309 | else {
|
310 | acc[processedKey] = processedValue;
|
311 | }
|
312 | return acc;
|
313 | }, {}));
|
314 | }
|
315 | else {
|
316 | const pvjsonValue = getPvjsonValue(gpmlElement, gpmlKey, gpmlValue);
|
317 | // NOTE: we don't include key/value pairs when the value is missing
|
318 | if (VALUES_TO_SKIP.indexOf(pvjsonValue) === -1) {
|
319 | return [[pvjsonKey || camelCase(gpmlKey), pvjsonValue]];
|
320 | }
|
321 | else {
|
322 | return [];
|
323 | }
|
324 | }
|
325 | }
|
326 | catch (err) {
|
327 | throw new VError(err, ` when calling processor.processKV(
|
328 | gpmlElement: ${JSON.stringify(gpmlElement, null, " ")},
|
329 | [
|
330 | gpmlKey: ${gpmlKey},
|
331 | gpmlValue: ${JSON.stringify(gpmlValue, null, " ")}
|
332 | ]
|
333 | )
|
334 | `);
|
335 | }
|
336 | });
|
337 | const { graphIdToZIndex, graphIdsByGraphRef, promisedGPMLElementByGraphId, gpmlElementStream, promisedGraphIdByGroupId, groupIdToGraphIdStream, promisedPvjsonEntityLatestByGraphId, pvjsonEntityLatestStream } = this;
|
338 | this.KeyMappings = KeyMappings;
|
339 | this.KeyValueConverters = KeyValueConverters;
|
340 | this.ValueMappings = ValueMappings;
|
341 | this.ValueConverters = ValueConverters;
|
342 | groupIdToGraphIdStream.each(function ([groupId, graphId]) {
|
343 | promisedGraphIdByGroupId[groupId] = Promise.resolve(graphId);
|
344 | });
|
345 | gpmlElementStream.each(function (gpmlElement) {
|
346 | promisedGPMLElementByGraphId[gpmlElement.GraphId] = Promise.resolve(gpmlElement);
|
347 | });
|
348 | pvjsonEntityLatestStream
|
349 | .doto(function (pvjsonEntity) {
|
350 | const { id, zIndex } = pvjsonEntity;
|
351 | graphIdToZIndex[id] = zIndex;
|
352 | promisedPvjsonEntityLatestByGraphId[id] = Promise.resolve(pvjsonEntity);
|
353 | })
|
354 | .errors(function (err, push) {
|
355 | push(new VError(err, ` observed in pvjsonEntityLatestStream
|
356 | `));
|
357 | })
|
358 | .each(function (pvjsonEntity) { });
|
359 | /*
|
360 | TODO do we need this?
|
361 | endStream.each(function(x) {
|
362 | groupIdToGraphIdStream.end();
|
363 | gpmlElementStream.end();
|
364 | pvjsonEntityLatestStream.end();
|
365 | });
|
366 | //*/
|
367 | }
|
368 | }
|
369 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUHJvY2Vzc29yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL1Byb2Nlc3Nvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLDZCQUE2QixDQUFDO0FBQ3JDLHVFQUF1RTtBQUV2RSxPQUFPLEVBQUUsWUFBWSxJQUFJLGFBQWEsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUN2RCxPQUFPLEVBRUwsU0FBUyxFQUNULE1BQU0sRUFDTixLQUFLLEVBR0wsU0FBUyxFQUVULE9BQU8sRUFDUCxRQUFRLEVBQ1IsUUFBUSxFQUlSLFNBQVMsRUFDVixNQUFNLFdBQVcsQ0FBQztBQUNuQixPQUFPLEtBQUssRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUMvQixPQUFPLEtBQUssTUFBTSxNQUFNLFFBQVEsQ0FBQztBQUNqQyxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sQ0FBQyxTQUFTLENBQUM7SUFDaEIseUZBQXlGO0lBQ3pGLG9HQUFvRztJQUNwRyxlQUFlO0lBQ2YsZ0JBQWdCLEVBQUUsSUFBSTtDQUN2QixDQUFDLENBQUM7QUFhSCxPQUFPLEVBQUUsYUFBYSxFQUFFLFFBQVEsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzNELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUVsRCxNQUFNLGdDQUFnQyxHQUFHO0lBQ3ZDLE1BQU0sRUFBRSxNQUFNO0lBQ2QsU0FBUyxFQUFFLFVBQVU7SUFDckIsUUFBUSxFQUFFLGdCQUFnQjtJQUMxQixhQUFhLEVBQUUsTUFBTTtJQUNyQixLQUFLLEVBQUUsT0FBTztJQUNkLFdBQVcsRUFBRSxNQUFNO0lBQ25CLEtBQUssRUFBRSxnQkFBZ0I7SUFDdkIsbUNBQW1DO0lBQ25DLDBCQUEwQjtJQUMxQixLQUFLLEVBQUUsZ0JBQWdCO0lBQ3ZCLEtBQUssRUFBRSxNQUFNO0NBQ2QsQ0FBQztBQUVGLE1BQU0sY0FBYyxHQUFHLENBQUMsRUFBRSxFQUFFLElB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 |
\ | No newline at end of file |