View Javadoc
1   /*
2    * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
3    * Copyright (C) 2010  Mickael Guessant
4    *
5    * This program is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU General Public License
7    * as published by the Free Software Foundation; either version 2
8    * of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with this program; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18   */
19  
20  package davmail.exchange.graph;
21  
22  import org.apache.log4j.Logger;
23  
24  import java.util.HashMap;
25  import java.util.Map;
26  
27  /**
28   * Map field names to actual graph properties.
29   * Properties can be native graph properties or extended properties mapped from MAPI ids.
30   * Some properties are searchable only through MAPI, others are straightforward.
31   */
32  public class GraphField {
33      protected static final Logger LOGGER = Logger.getLogger("davmail.exchange.graph.GraphField");
34  
35      @SuppressWarnings({"UnusedDeclaration"})
36      protected enum PropertyType {
37          ApplicationTime, ApplicationTimeArray, Binary, BinaryArray, Boolean, CLSID, CLSIDArray, Currency, CurrencyArray,
38          Double, DoubleArray, Error, Float, FloatArray, Integer, IntegerArray, Long, LongArray, Null, Object,
39          ObjectArray, Short, ShortArray, SystemTime, SystemTimeArray, String, StringArray
40      }
41  
42      @SuppressWarnings({"UnusedDeclaration"})
43      public enum DistinguishedPropertySetType {
44          Meeting, Appointment, Common, PublicStrings, Address, InternetHeaders, CalendarAssistant, UnifiedMessaging, Task
45      }
46  
47      private static final Map<String, GraphField> FIELD_MAP = new HashMap<>();
48  
49      static {
50          addFieldMap("id");
51  
52          // folder extended properties
53          addFieldMap("folderlastmodified", 0x3008, PropertyType.SystemTime);
54          addFieldMap("folderclass", 0x3613, PropertyType.String);
55          addFieldMap("ctag", 0x670a, PropertyType.SystemTime); // PR_LOCAL_COMMIT_TIME_MAX
56          addFieldMap("uidNext", 0x6751, PropertyType.Integer); // PR_ARTICLE_NUM_NEXT
57  
58          // message properties
59          addFieldMap("isRead", PropertyType.Boolean);
60          addFieldMap("isDraft");
61          addFieldMap("receivedDateTime", PropertyType.SystemTime);
62          addFieldMap("datereceived", "receivedDateTime", PropertyType.SystemTime);
63          addFieldMap("date", "receivedDateTime", PropertyType.SystemTime);
64          // addFieldMap("date", 0x0e06, PropertyType.SystemTime);  // PidTagOriginalDeliveryTime
65  
66          // MAPI origin properties: Graph ignores receivedDateTime, sentDateTime and
67          // internetMessageHeaders when a message is created, but accepts these
68          addFieldMap("deliverytime", 0x0e06, PropertyType.SystemTime);    // PR_MESSAGE_DELIVERY_TIME
69          addFieldMap("clientsubmittime", 0x0039, PropertyType.SystemTime); // PR_CLIENT_SUBMIT_TIME
70          addFieldMap("inreplyto", 0x1042, PropertyType.String);            // PR_IN_REPLY_TO_ID
71          addFieldMap("references", 0x1039, PropertyType.String);           // PR_INTERNET_REFERENCES
72          addFieldMap("lastmodified", "lastModifiedDateTime", PropertyType.SystemTime);
73  
74          // message extended properties
75          addFieldMap("uid", 0x0FF9, PropertyType.Binary); // PR_RECORD_KEY
76          addFieldMap("messageFlags", 0x0e07, PropertyType.Integer); // PR_MESSAGE_FLAGS
77          addFieldMap("imapUid", 0x0e23, PropertyType.Integer);
78          addFieldMap("messageSize", 0x0e08, PropertyType.Integer);
79          addFieldMap("etag", 0x3008, PropertyType.SystemTime);
80          addFieldMap("contentclass", DistinguishedPropertySetType.InternetHeaders, "content-class");
81          addFieldMap("iconIndex", 0x1080, PropertyType.Integer); // PR_ICON_INDEX
82  
83          addFieldMap("keywords", "categories", PropertyType.StringArray); // special case, mapped to message categories
84          addFieldMap( "categories", PropertyType.StringArray);
85  
86          addFieldMap("@odata.etag");
87          addFieldMap("changeKey", "@odata.etag");
88  
89          addFieldMap("read", "isRead", PropertyType.Boolean);
90          addFieldMap("messageheaders", 0x007D, PropertyType.String); // PR_TRANSPORT_MESSAGE_HEADERS
91          addFieldMap("internetMessageHeaders");
92  
93          addFieldMap("to", DistinguishedPropertySetType.InternetHeaders, "to");
94          addFieldMap("cc", DistinguishedPropertySetType.InternetHeaders, "cc");
95          addFieldMap("from", DistinguishedPropertySetType.InternetHeaders, "from");
96  
97          addFieldMap("permanenturl", 0x670E, PropertyType.String); //PR_FLAT_URL_NAME
98          addFieldMap("lastVerbExecuted", 0x1081, PropertyType.Integer); // PR_ACTION_FLAG
99          addFieldMap("junk", 0x1083, PropertyType.Integer);
100         addFieldMap("flagStatus", 0x1090, PropertyType.Integer); // PidTagFlagStatus
101         addFieldMap("deleted", DistinguishedPropertySetType.Common, 0x8570, PropertyType.Integer); // PidLidImapDeleted
102 
103         addFieldMap("urlcompname", 0x10f3, PropertyType.String);
104 
105         // events and tasks https://learn.microsoft.com/en-us/graph/api/resources/event?view=graph-rest-beta
106 
107         addFieldMap("processed",  0x65e8, PropertyType.Boolean);
108 
109         addFieldMap("instancetype", DistinguishedPropertySetType.PublicStrings, "urn:schemas:calendar:instancetype", PropertyType.Integer);
110 
111         // PidLidOwnerCriticalChange: date/time the organizer sent/updated the meeting (used for DTSTAMP per MS-OXCICAL)
112         addFieldMap("dtstamp", DistinguishedPropertySetType.Meeting, 0x001A, PropertyType.SystemTime);
113 
114         addFieldMap("iCalUid");
115         addFieldMap("calendaruid", DistinguishedPropertySetType.PublicStrings, "urn:schemas:calendar:uid", PropertyType.String);
116 
117         addFieldMap("isrecurring", DistinguishedPropertySetType.Appointment, 0x8223, PropertyType.Boolean); // PidLidRecurring
118 
119         addFieldMap("dtstart", "start");
120         addFieldMap("dtend", "end");
121         addFieldMap("subject");
122         addFieldMap("status");
123         addFieldMap("taskstatus");
124         addFieldMap("importance");
125         addFieldMap("createdDateTime", "createdDateTime", PropertyType.SystemTime);
126         addFieldMap("lastModifiedDateTime", "lastModifiedDateTime", PropertyType.SystemTime);
127 
128         // tasks
129         addFieldMap("summary", "title"); // title conflicts with job title
130         addFieldMap("dueDateTime");
131         addFieldMap("startDateTime");
132         addFieldMap("completedDateTime");
133         addFieldMap("datecompleted", "completedDateTime");
134         addFieldMap("objecttype");
135 
136         addFieldMap("originalStartTimeZone");
137 
138         addFieldMap("allowNewTimeProposals");
139         addFieldMap("attendees");
140         addFieldMap("bodyPreview");
141         addFieldMap("body");
142         addFieldMap("end");
143         addFieldMap("exceptionOccurrences");
144         addFieldMap("cancelledOccurrences");
145         addFieldMap("hasAttachments");
146         addFieldMap("isOnlineMeeting");
147         addFieldMap("onlineMeeting");
148         addFieldMap("isOrganizer");
149         addFieldMap("location");
150         addFieldMap("locations"); // locations array
151         addFieldMap("organizer");
152         addFieldMap("originalStart");
153         addFieldMap("recurrence");
154         addFieldMap("reminderMinutesBeforeStart");
155         addFieldMap("start");
156         addFieldMap("type");
157 
158         addFieldMap("iCalUId");
159         addFieldMap("transactionId");
160         //addFieldMap("iCalUId", DistinguishedPropertySetType.Meeting, 0x023, PropertyType.Binary); // PidLidGlobalObjectId
161 
162         addFieldMap("showAs");
163         addFieldMap("isAllDay", "isAllDay", PropertyType.Boolean);
164         addFieldMap("isCancelled", "isCancelled", PropertyType.Boolean);
165         addFieldMap("responseRequested");
166         addFieldMap("responseStatus");
167 
168         addFieldMap("isReminderOn", "isReminderOn", PropertyType.Boolean);
169 
170         addFieldMap("xmozsendinvitations", DistinguishedPropertySetType.PublicStrings, "xmozsendinvitations");
171         addFieldMap("xmozlastack", DistinguishedPropertySetType.PublicStrings, "xmozlastack");
172         addFieldMap("xmozsnoozetime", DistinguishedPropertySetType.PublicStrings, "xmozsnoozetime");
173 
174         // contacts https://learn.microsoft.com/en-us/graph/api/resources/contact
175         addFieldMap("displayname", "displayName"); // MAPI addFieldMap("displayname", 0x3001, PropertyType.String);
176 
177         addFieldMap("outlookmessageclass", 0x001A, PropertyType.String);
178         addFieldMap("fileas", "fileAs");
179 
180         addFieldMap("cn", "displayName");
181         addFieldMap("sn", "surname");
182         addFieldMap("givenName", "givenName");
183         addFieldMap("middlename", "middleName");
184         addFieldMap("personaltitle", "title"); // MAPI addFieldMap("personaltitle", 0x3A45, PropertyType.String);
185         addFieldMap("title", "jobTitle"); // MAPI /addFieldMap("title", 0x3A17, PropertyType.String);
186 
187         // map description to PR_BODY and personalnotes to native personalNotes
188         addFieldMap("description", 0x1000, PropertyType.String);
189         addFieldMap("personalNotes");
190 
191         // for distribution lists
192         addFieldMap("notes");
193         addFieldMap("displayName");
194 
195         addFieldMap("namesuffix", "generation");
196         addFieldMap("nickname", "nickName");
197         addFieldMap("mobile", 0x3A1C, PropertyType.String);
198         addFieldMap("telephoneNumber", 0x3A08, PropertyType.String); // PidTagBusinessTelephoneNumber
199 
200         addFieldMap("otherBusinessTelephoneNumber", 0x3A1B, PropertyType.String); // PidTagBusiness2TelephoneNumber
201 
202         addFieldMap("facsimiletelephonenumber", 0x3A24, PropertyType.String);
203         addFieldMap("pager", 0x3A21, PropertyType.String);
204 
205         addFieldMap("homeCity", 0x3A59, PropertyType.String);
206         addFieldMap("homeCountry", 0x3A5A, PropertyType.String);
207         addFieldMap("homePhone", 0x3A09, PropertyType.String);
208 
209         addFieldMap("otherHomePhone", 0x3A2F, PropertyType.String); // PidTagHome2TelephoneNumber
210 
211         addFieldMap("homePostalCode", 0x3A5B, PropertyType.String);
212         addFieldMap("homeState", 0x3A5C, PropertyType.String);
213         addFieldMap("homeStreet", 0x3A5D, PropertyType.String);
214         addFieldMap("homepostofficebox", 0x3A5E, PropertyType.String);
215 
216         addFieldMap("postofficebox", DistinguishedPropertySetType.Address, 0x804A, PropertyType.String);
217         addFieldMap("roomnumber", "officeLocation"); // MAPI addFieldMap("roomnumber", 0x3A19, PropertyType.String);
218         addFieldMap("street", DistinguishedPropertySetType.Address, 0x8045, PropertyType.String);
219 
220         addFieldMap("l", DistinguishedPropertySetType.Address, 0x8046, PropertyType.String);
221         addFieldMap("st", DistinguishedPropertySetType.Address, 0x8047, PropertyType.String);
222         addFieldMap("postalcode", DistinguishedPropertySetType.Address, 0x8048, PropertyType.String);
223         addFieldMap("co", DistinguishedPropertySetType.Address, 0x8049, PropertyType.String);
224 
225         // addFieldMap("o", DistinguishedPropertySetType.Address, 0x3A16, PropertyType.String); ??
226         //addFieldMap("o", DistinguishedPropertySetType.Address, 0x3A18, PropertyType.String); // department
227         addFieldMap("o", "companyName"); // department
228 
229         addFieldMap("department", "department");// MAPI addFieldMap("department", 0x3A18, PropertyType.String);
230 
231         addFieldMap("smtpemail1", DistinguishedPropertySetType.Address, 0x8083, PropertyType.String); // Email1EmailAddress
232         addFieldMap("smtpemail2", DistinguishedPropertySetType.Address, 0x8093, PropertyType.String); // Email2EmailAddress
233         addFieldMap("smtpemail3", DistinguishedPropertySetType.Address, 0x80A3, PropertyType.String); // Email3EmailAddress
234 
235         addFieldMap("businesshomepage", 0x3A51, PropertyType.String);
236         addFieldMap("personalHomePage", 0x3A50, PropertyType.String);
237 
238 
239         addFieldMap("extensionattribute1", DistinguishedPropertySetType.Address, 0x804F, PropertyType.String);
240         addFieldMap("extensionattribute2", DistinguishedPropertySetType.Address, 0x8050, PropertyType.String);
241         addFieldMap("extensionattribute3", DistinguishedPropertySetType.Address, 0x8051, PropertyType.String);
242         addFieldMap("extensionattribute4", DistinguishedPropertySetType.Address, 0x8052, PropertyType.String);
243 
244         addFieldMap("bday", "birthday", PropertyType.SystemTime); // MAPI addFieldMap("bday", DistinguishedPropertySetType.Address, 0x3A42, PropertyType.SystemTime);
245         addFieldMap("anniversary", "weddingAnniversary", PropertyType.SystemTime);  // MAPI addFieldMap("anniversary", DistinguishedPropertySetType.Address, 0x3A41, PropertyType.SystemTime);
246 
247         addFieldMap("otherstreet", 0x3A63, PropertyType.String);
248         addFieldMap("otherstate", 0x3A62, PropertyType.String);
249         addFieldMap("otherpostofficebox", 0x3A64, PropertyType.String);
250         addFieldMap("otherpostalcode", 0x3A61, PropertyType.String);
251         addFieldMap("othercountry", 0x3A60, PropertyType.String);
252         addFieldMap("othercity", 0x3A5F, PropertyType.String);
253 
254         addFieldMap("secretarycn", "assistantName"); // MAPI addFieldMap("secretarycn", 0x3A30, PropertyType.String);
255         addFieldMap("spousecn", "spouseName"); // MAPI addFieldMap("spousecn", 0x3A48, PropertyType.String);
256 
257         addFieldMap("private", DistinguishedPropertySetType.Common, 0x8506, PropertyType.Boolean);
258 
259         addFieldMap("im", DistinguishedPropertySetType.Address, 0x8062, PropertyType.String);
260         addFieldMap("fburl", DistinguishedPropertySetType.Address, 0x80D8, PropertyType.String);
261 
262         addFieldMap("haspicture", DistinguishedPropertySetType.Address, 0x8015, PropertyType.Boolean);
263 
264         addFieldMap("manager");// MAPI addFieldMap("manager", 0x3A4E, PropertyType.String);
265         addFieldMap("profession"); // MAPI addFieldMap("profession", 0x3A46, PropertyType.String);
266 
267         addFieldMap("othermobile", 0x3A1E, PropertyType.String); // PidTagCarTelephoneNumber
268         addFieldMap("otherTelephone", 0x3A1F, PropertyType.String); // PidTagOtherTelephoneNumber
269 
270         addFieldMap("homefax", 0x3A25, PropertyType.String); // PidTagHomeFaxNumber
271         addFieldMap("internationalisdnnumber", 0x3A2D, PropertyType.String); // PidTagIsdnNumber
272 
273         addFieldMap("gender");// MAPI addFieldMap("gender", 0x3A4D, PropertyType.Short);
274 
275         addFieldMap("sensitivity", 0x0036, PropertyType.Integer);
276 
277         // for events map class to sensitivity
278         addFieldMap("class", "sensitivity");
279 
280         // does not map to anything over graph
281         addFieldMap("msexchangecertificate");
282         addFieldMap("usersmimecertificate");
283 
284     }
285 
286     protected static void addFieldMap(String alias, GraphField field) {
287         if (FIELD_MAP.containsKey(alias)) {
288             throw new IllegalArgumentException("Duplicate field alias: " + alias);
289         }
290         FIELD_MAP.put(alias, field);
291     }
292 
293     protected static void addFieldMap(String alias) {
294         addFieldMap(alias, new GraphField(alias));
295     }
296 
297     protected static void addFieldMap(String alias, String graphId) {
298         addFieldMap(alias, new GraphField(alias, graphId));
299     }
300 
301     protected static void addFieldMap(String alias, PropertyType propertyType) {
302         addFieldMap(alias, new GraphField(alias, alias, propertyType));
303     }
304 
305     protected static void addFieldMap(String alias, String graphId, PropertyType propertyType) {
306         addFieldMap(alias, new GraphField(alias, graphId, propertyType));
307     }
308 
309     protected static void addFieldMap(String alias, int intPropertyTag, PropertyType propertyType) {
310         addFieldMap(alias, new GraphField(alias, intPropertyTag, propertyType));
311     }
312 
313     protected static void addFieldMap(String alias, DistinguishedPropertySetType distinguishedPropertySetId, int intPropertyTag, PropertyType propertyType) {
314         addFieldMap(alias, new GraphField(alias, distinguishedPropertySetId, intPropertyTag, propertyType));
315     }
316 
317     protected static void addFieldMap(String alias, DistinguishedPropertySetType distinguishedPropertySetId, String propertyName, PropertyType propertyType) {
318         addFieldMap(alias, new GraphField(alias, distinguishedPropertySetId, propertyName, propertyType));
319     }
320 
321     protected static void addFieldMap(String alias, DistinguishedPropertySetType distinguishedPropertySetId, String propertyName) {
322         addFieldMap(alias, new GraphField(alias, distinguishedPropertySetId, propertyName));
323     }
324 
325     protected String alias;
326     protected String graphId;
327 
328     protected DistinguishedPropertySetType distinguishedPropertySetId;
329 
330     protected String propertyName;
331     protected int propertyId;
332     protected String propertyTag;
333     protected PropertyType propertyType;
334 
335 
336     private boolean extended = false;
337 
338     /**
339      * Basic graph field.
340      * @param alias property alias
341      */
342     public GraphField(String alias) {
343         this.alias = alias;
344         this.graphId = alias;
345     }
346 
347     public GraphField(String alias, String graphId) {
348         this.alias = alias;
349         this.graphId = graphId;
350     }
351 
352 
353     protected GraphField(String alias, String graphId, PropertyType propertyType) {
354         this.alias = alias;
355         this.graphId = graphId;
356         this.propertyType = propertyType;
357     }
358 
359     /**
360      * Header field or categories field.
361      * @param alias property alias
362      * @param distinguishedPropertySetId property type
363      */
364     public GraphField(String alias, DistinguishedPropertySetType distinguishedPropertySetId, String propertyName) {
365         this.extended = true;
366 
367         this.alias = alias;
368         this.propertyType = PropertyType.String;
369         this.distinguishedPropertySetId = distinguishedPropertySetId;
370         this.propertyName = propertyName;
371         this.graphId = buildGraphId();
372     }
373 
374     /**
375      * Create an extended field.
376      *
377      * @param alias          property alias
378      * @param intPropertyTag property tag as int
379      * @param propertyType   property type
380      */
381     protected GraphField(String alias, int intPropertyTag, PropertyType propertyType) {
382         this.extended = true;
383 
384         this.alias = alias;
385         this.propertyTag = "0x" + Integer.toHexString(intPropertyTag);
386         this.propertyType = propertyType;
387         this.graphId = buildGraphId();
388     }
389 
390     protected GraphField(String alias, DistinguishedPropertySetType distinguishedPropertySetId, int propertyId, PropertyType propertyType) {
391         this.extended = true;
392 
393         this.alias = alias;
394         this.distinguishedPropertySetId = distinguishedPropertySetId;
395         this.propertyType = propertyType;
396         this.propertyId = propertyId;
397         this.graphId = buildGraphId();
398     }
399 
400     protected GraphField(String alias, DistinguishedPropertySetType distinguishedPropertySetId, String propertyName, PropertyType propertyType) {
401         this.extended = true;
402 
403         this.alias = alias;
404         this.distinguishedPropertySetId = distinguishedPropertySetId;
405         this.propertyName = propertyName;
406         this.propertyType = propertyType;
407         this.graphId = buildGraphId();
408     }
409 
410     /**
411      * Builds a graph identifier based on the property type, namespace, and associated property details.
412      * The resulting identifier is formatted based on specific rules that include the property type,
413      * namespace GUID (if available), and either the property name or property ID, or in certain cases, the property tag.
414      * See <a href="https://learn.microsoft.com/en-us/graph/api/resources/extended-properties-overview">Outlook extended properties overview</a>
415      *
416      * @return the constructed graph identifier as a String
417      */
418     private String buildGraphId() {
419         // PropertyId values may only be in one of the following formats:
420         // 'MapiPropertyType namespaceGuid Name propertyName', 'MapiPropertyType namespaceGuid Id propertyId' or 'MapiPropertyType propertyTag'.
421 
422         String namespaceGuid = null;
423         if (distinguishedPropertySetId != null) {
424             switch (distinguishedPropertySetId) {
425                 case PublicStrings:
426                     namespaceGuid = "{00020329-0000-0000-c000-000000000046}";
427                     break;
428                 case InternetHeaders:
429                     namespaceGuid = "{00020386-0000-0000-c000-000000000046}";
430                     break;
431                 case Common:
432                     namespaceGuid = "{00062008-0000-0000-c000-000000000046}";
433                     break;
434                 case Address:
435                     namespaceGuid = "{00062004-0000-0000-c000-000000000046}";
436                     break;
437                 case Appointment:
438                     namespaceGuid = "{00062002-0000-0000-C000-000000000046}";
439                     break;
440                 case Meeting:
441                     namespaceGuid = "{6ED8DA90-450B-101B-98DA-00AA003F1305}";
442                     break;
443                 case Task:
444                     namespaceGuid = "{00062003-0000-0000-c000-000000000046}";
445                     break;
446             }
447         }
448 
449 
450         StringBuilder buffer = new StringBuilder();
451         if (namespaceGuid != null) {
452             buffer.append(propertyType.name()).append(" ").append(namespaceGuid);
453             if (propertyName != null) {
454                 buffer.append(" Name ").append(propertyName);
455             } else {
456                 buffer.append(" Id ").append("0x").append(Integer.toHexString(propertyId));
457             }
458         } else if (propertyTag != null) {
459             buffer.append(propertyType.name()).append(" ").append(propertyTag);
460         }
461         return buffer.toString();
462     }
463 
464     public boolean isExtended() {
465         return extended;
466     }
467 
468     public String getAlias() {
469         return alias;
470     }
471 
472     public String getGraphId() {
473         if (graphId != null) {
474             return graphId;
475         }
476         throw new IllegalStateException("Graph id not set on field " + alias);
477         //return alias;
478     }
479 
480     public boolean isMultiValued() {
481         return propertyType == PropertyType.StringArray;
482     }
483 
484     public boolean isNumber() {
485         return propertyType == PropertyType.Short || propertyType == PropertyType.Integer || propertyType == PropertyType.Long || propertyType == PropertyType.Double;
486     }
487 
488     public boolean isBinary() {
489         return propertyType == PropertyType.Binary;
490     }
491 
492     public boolean isBoolean() {
493         return propertyType == PropertyType.Boolean;
494     }
495 
496     public boolean isDate() {
497         return propertyType == PropertyType.SystemTime;
498     }
499 
500     public boolean isInternetHeaders() {
501         return distinguishedPropertySetId == DistinguishedPropertySetType.InternetHeaders;
502     }
503 
504     /**
505      * Get field by alias.
506      * @param alias property alias
507      * @return field definition
508      */
509     public static GraphField get(String alias) {
510         if (!FIELD_MAP.containsKey(alias)) {
511             LOGGER.warn("Missing mapping for " + alias);
512             return new GraphField(alias);
513         }
514         return FIELD_MAP.get(alias);
515     }
516 
517     public static String getGraphId(String alias) {
518         return get(alias).getGraphId();
519     }
520 
521 }