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 fill (background color).
|
46 | // It's always just white.
|
47 | fill: "white",
|
48 | strokeWidth: 0,
|
49 | stroke: "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 | entitiesById: {}
|
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 | // TODO: why doesn't the following work?
|
191 | //return entity as PvjsonEntity & Comment;
|
192 | return entity;
|
193 | });
|
194 | this.processPropertiesAndType = curry((gpmlElementName, gpmlElement) => {
|
195 | const pvjsonEntity = this.processType(gpmlElementName, this.processProperties(gpmlElement));
|
196 | this.pvjsonEntityLatestStream.write(pvjsonEntity);
|
197 | return pvjsonEntity;
|
198 | });
|
199 | this.processType = curry((gpmlElementName, processed) => {
|
200 | const kaavioType = GPML_ELEMENT_NAME_TO_KAAVIO_TYPE[gpmlElementName];
|
201 | processed.type = unionLSV(processed.type, gpmlElementName, kaavioType);
|
202 | if (processed.xrefDataSource && processed.xrefIdentifier) {
|
203 | processed.type = unionLSV(processed.type, `${processed.xrefDataSource}:${processed.xrefIdentifier}`);
|
204 | }
|
205 | if (!!kaavioType) {
|
206 | processed.kaavioType = kaavioType;
|
207 | }
|
208 | processed.gpmlElementName = gpmlElementName;
|
209 | return processed;
|
210 | });
|
211 | this.setPvjsonEntity = pvjsonEntity => {
|
212 | const { graphIdToZIndex, promisedPvjsonEntityLatestByGraphId } = this;
|
213 | const { id, zIndex } = pvjsonEntity;
|
214 | graphIdToZIndex[id] = zIndex;
|
215 | promisedPvjsonEntityLatestByGraphId[id] = Promise.resolve(pvjsonEntity);
|
216 | this.output = iassign(this.output, function (o) {
|
217 | return o.entitiesById;
|
218 | }, function (entitiesById) {
|
219 | entitiesById[pvjsonEntity.id] = pvjsonEntity;
|
220 | return entitiesById;
|
221 | });
|
222 | };
|
223 | this.getGPMLKeyAsJSFunctionName = (gpmlKey) => {
|
224 | // NOTE: gpmlKeyAsJSFunctionName is for attributes like "Data-Source", because
|
225 | // the following would be invalid JS:
|
226 | // export function Data-Source() {};
|
227 | // TODO what about things like spaces, etc.?
|
228 | return gpmlKey.replace("-", "");
|
229 | };
|
230 | this.getPvjsonValue = (gpmlElement, gpmlKey, gpmlValue) => {
|
231 | const { getGPMLKeyAsJSFunctionName, getPvjsonValue, processKV, ValueConverters, ValueMappings } = this;
|
232 | const gpmlKeyAsJSFunctionName = getGPMLKeyAsJSFunctionName(gpmlKey);
|
233 | let pvjsonValue;
|
234 | try {
|
235 | if (ValueConverters.hasOwnProperty(gpmlKeyAsJSFunctionName)) {
|
236 | return ValueConverters[gpmlKeyAsJSFunctionName](gpmlElement);
|
237 | }
|
238 | else if (isStringTS(gpmlValue)) {
|
239 | if (ValueMappings.hasOwnProperty(gpmlValue)) {
|
240 | return ValueMappings[gpmlValue];
|
241 | }
|
242 | else {
|
243 | return gpmlValue;
|
244 | }
|
245 | }
|
246 | else if (isArrayTS(gpmlValue)) {
|
247 | return gpmlValue.map(valueItem => {
|
248 | return getPvjsonValue(valueItem, gpmlKey, valueItem);
|
249 | });
|
250 | }
|
251 | else if (isRecord(gpmlValue)) {
|
252 | return fromPairs(toPairsIn(gpmlValue).reduce((acc, [key, value]) => {
|
253 | processKV(gpmlValue, [key, value]).forEach(function (x) {
|
254 | acc.push(x);
|
255 | });
|
256 | return acc;
|
257 | }, []));
|
258 | }
|
259 | else {
|
260 | return gpmlValue;
|
261 | }
|
262 | }
|
263 | catch (err) {
|
264 | throw new VError(err, ` when calling
|
265 | getPvjsonValue(
|
266 | ${JSON.stringify(gpmlElement, null, "")},
|
267 | ${JSON.stringify(gpmlKey, null, "")},
|
268 | ${JSON.stringify(gpmlValue, null, "")}
|
269 | )
|
270 | `);
|
271 | }
|
272 | };
|
273 | this.processKV = curry((gpmlElement, [gpmlKey, gpmlValue]) => {
|
274 | try {
|
275 | const { getGPMLKeyAsJSFunctionName, getPvjsonValue, KeyMappings, KeyValueConverters, processKV, ValueMappings } = this;
|
276 | const gpmlKeyAsJSFunctionName = getGPMLKeyAsJSFunctionName(gpmlKey);
|
277 | if (VALUES_TO_SKIP.indexOf(gpmlValue) > -1) {
|
278 | return [];
|
279 | }
|
280 | if (KeyValueConverters.hasOwnProperty(gpmlKeyAsJSFunctionName)) {
|
281 | return KeyValueConverters[gpmlKeyAsJSFunctionName](gpmlElement, KeyMappings, ValueMappings);
|
282 | }
|
283 | const pvjsonKey = KeyMappings[gpmlKey];
|
284 | // NOTE "pvjson:merge" is for elements like "Graphics", where they
|
285 | // are nested in GPML but are merged into the parent in pvjson.
|
286 | if (gpmlKey[0] === "_" ||
|
287 | pvjsonKey === "pvjson:delete" ||
|
288 | (isObject(gpmlValue) && !isDefinedCXML(gpmlValue))) {
|
289 | // NOTE: we don't want to include "private" keys, such as
|
290 | // "_exists" or "_namespace".
|
291 | return [];
|
292 | }
|
293 | else if (pvjsonKey === "pvjson:merge") {
|
294 | return toPairsIn(gpmlValue).reduce((acc, pair) => concat(acc, processKV(gpmlElement, pair)), []);
|
295 | }
|
296 | else if (pvjsonKey === "pvjson:each") {
|
297 | // NOTE: Example of what uses this is GPML Attribute.
|
298 | // (in GPML, 'Attribute' is an XML *ELEMENT* named "Attribute")
|
299 | return toPairsIn(gpmlValue
|
300 | // NOTE: some attributes have empty values and will cause problems
|
301 | // if we don't use this filter to skip them.
|
302 | .filter(({ Key, Value }) => VALUES_TO_SKIP.indexOf(Value) === -1)
|
303 | .map(({ Key, Value }) => {
|
304 | return processKV(gpmlElement, [Key, Value]);
|
305 | })
|
306 | .reduce((acc, [[processedKey, processedValue]]) => {
|
307 | // NOTE: this looks more complicated than it needs to be,
|
308 | // but it's to handle the case where there are two or more
|
309 | // sibling Attribute elements that share the same Key.
|
310 | // I don't know of any cases of this in our actual GPML,
|
311 | // but the XSD does not require unique Keys for sibling
|
312 | // Attributes.
|
313 | if (acc.hasOwnProperty(processedKey)) {
|
314 | acc[processedKey] = unionLSV(acc[processedKey], processedValue);
|
315 | }
|
316 | else {
|
317 | acc[processedKey] = processedValue;
|
318 | }
|
319 | return acc;
|
320 | }, {}));
|
321 | }
|
322 | else {
|
323 | const pvjsonValue = getPvjsonValue(gpmlElement, gpmlKey, gpmlValue);
|
324 | // NOTE: we don't include key/value pairs when the value is missing
|
325 | if (VALUES_TO_SKIP.indexOf(pvjsonValue) === -1) {
|
326 | return [[pvjsonKey || camelCase(gpmlKey), pvjsonValue]];
|
327 | }
|
328 | else {
|
329 | return [];
|
330 | }
|
331 | }
|
332 | }
|
333 | catch (err) {
|
334 | throw new VError(err, ` when calling processor.processKV(
|
335 | gpmlElement: ${JSON.stringify(gpmlElement, null, " ")},
|
336 | [
|
337 | gpmlKey: ${gpmlKey},
|
338 | gpmlValue: ${JSON.stringify(gpmlValue, null, " ")}
|
339 | ]
|
340 | )
|
341 | `);
|
342 | }
|
343 | });
|
344 | const { graphIdToZIndex, graphIdsByGraphRef, promisedGPMLElementByGraphId, gpmlElementStream, promisedGraphIdByGroupId, groupIdToGraphIdStream, promisedPvjsonEntityLatestByGraphId, pvjsonEntityLatestStream } = this;
|
345 | this.KeyMappings = KeyMappings;
|
346 | this.KeyValueConverters = KeyValueConverters;
|
347 | this.ValueMappings = ValueMappings;
|
348 | this.ValueConverters = ValueConverters;
|
349 | groupIdToGraphIdStream.each(function ([groupId, graphId]) {
|
350 | promisedGraphIdByGroupId[groupId] = Promise.resolve(graphId);
|
351 | });
|
352 | gpmlElementStream.each(function (gpmlElement) {
|
353 | promisedGPMLElementByGraphId[gpmlElement.GraphId] = Promise.resolve(gpmlElement);
|
354 | });
|
355 | pvjsonEntityLatestStream
|
356 | .doto(function (pvjsonEntity) {
|
357 | const { id, zIndex } = pvjsonEntity;
|
358 | graphIdToZIndex[id] = zIndex;
|
359 | promisedPvjsonEntityLatestByGraphId[id] = Promise.resolve(pvjsonEntity);
|
360 | })
|
361 | .errors(function (err, push) {
|
362 | push(new VError(err, ` observed in pvjsonEntityLatestStream
|
363 | `));
|
364 | })
|
365 | .each(function (pvjsonEntity) { });
|
366 | /*
|
367 | TODO do we need this?
|
368 | endStream.each(function(x) {
|
369 | groupIdToGraphIdStream.end();
|
370 | gpmlElementStream.end();
|
371 | pvjsonEntityLatestStream.end();
|
372 | });
|
373 | //*/
|
374 | }
|
375 | }
|
376 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUHJvY2Vzc29yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL1Byb2Nlc3Nvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLDZCQUE2QixDQUFDO0FBQ3JDLHVFQUF1RTtBQUV2RSxPQUFPLEVBQUUsWUFBWSxJQUFJLGFBQWEsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUN2RCxPQUFPLEVBRUwsU0FBUyxFQUNULE1BQU0sRUFDTixLQUFLLEVBR0wsU0FBUyxFQUVULE9BQU8sRUFDUCxRQUFRLEVBQ1IsUUFBUSxFQUlSLFNBQVMsRUFDVixNQUFNLFdBQVcsQ0FBQztBQUNuQixPQUFPLEtBQUssRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUMvQixPQUFPLEtBQUssTUFBTSxNQUFNLFFBQVEsQ0FBQztBQUNqQyxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sQ0FBQyxTQUFTLENBQUM7SUFDaEIseUZBQXlGO0lBQ3pGLG9HQUFvRztJQUNwRyxlQUFlO0lBQ2YsZ0JBQWdCLEVBQUUsSUFBSTtDQUN2QixDQUFDLENBQUM7QUFhSCxPQUFPLEVBQUUsYUFBYSxFQUFFLFFBQVEsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzNELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUVsRCxNQUFNLGdDQUFnQyxHQUFHO0lBQ3ZDLE1BQU0sRUFBRSxNQUFNO0lBQ2QsU0FBUyxFQUFFLFVBQVU7SUFDckIsUUFBUSxFQUFFLGdCQUFnQjtJQUMxQixhQUFhLEVBQUUsTUFBTTtJQUNyQixLQUFLLEVBQUUsT0FBTztJQUNkLFdBQVcsRUFBRSxNQUFNO0lBQ25CLEtBQUssRUFBRSxnQkFBZ0I7SUFDdkIsbUNBQW1DO0lBQ25DLDBCQUEwQjtJQUMxQixLQUFLLEVBQUUsZ0JBQWdCO0lBQ3ZCLEtBQUssRUFBRSxNQUFNO0NBQ2QsQ0FBQztBQUVGLE1BQU0sY0FBYyxHQUFHLENBQUMsRUFBRSxFQUFFLElB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 |
\ | No newline at end of file |