View Javadoc
1   package ca.uhn.fhir.model.primitive;
2   
3   import ca.uhn.fhir.model.api.IResource;
4   import ca.uhn.fhir.model.api.annotation.DatatypeDef;
5   import ca.uhn.fhir.model.api.annotation.SimpleSetter;
6   import ca.uhn.fhir.parser.DataFormatException;
7   import ca.uhn.fhir.rest.api.Constants;
8   import ca.uhn.fhir.util.UrlUtil;
9   import org.apache.commons.lang3.ObjectUtils;
10  import org.apache.commons.lang3.StringUtils;
11  import org.apache.commons.lang3.Validate;
12  import org.apache.commons.lang3.builder.HashCodeBuilder;
13  import org.hl7.fhir.instance.model.api.IAnyResource;
14  import org.hl7.fhir.instance.model.api.IBaseResource;
15  import org.hl7.fhir.instance.model.api.IIdType;
16  
17  import java.math.BigDecimal;
18  import java.util.UUID;
19  
20  import static org.apache.commons.lang3.StringUtils.*;
21  
22  /*
23   * #%L
24   * HAPI FHIR - Core Library
25   * %%
26   * Copyright (C) 2014 - 2018 University Health Network
27   * %%
28   * Licensed under the Apache License, Version 2.0 (the "License");
29   * you may not use this file except in compliance with the License.
30   * You may obtain a copy of the License at
31   * 
32   * http://www.apache.org/licenses/LICENSE-2.0
33   * 
34   * Unless required by applicable law or agreed to in writing, software
35   * distributed under the License is distributed on an "AS IS" BASIS,
36   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
37   * See the License for the specific language governing permissions and
38   * limitations under the License.
39   * #L%
40   */
41  
42  /**
43   * Represents the FHIR ID type. This is the actual resource ID, meaning the ID that will be used in RESTful URLs, Resource References, etc. to represent a specific instance of a resource.
44   * <p>
45   * <p>
46   * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
47   * limit of 36 characters.
48   * </p>
49   * <p>
50   * regex: [a-z-Z0-9\-\.]{1,36}
51   * </p>
52   */
53  @DatatypeDef(name = "id", profileOf = StringDt.class)
54  public class IdDt extends UriDt implements /*IPrimitiveDatatype<String>, */IIdType {
55  
56  	private String myBaseUrl;
57  	private boolean myHaveComponentParts;
58  	private String myResourceType;
59  	private String myUnqualifiedId;
60  	private String myUnqualifiedVersionId;
61  
62  	/**
63  	 * Create a new empty ID
64  	 */
65  	public IdDt() {
66  		super();
67  	}
68  
69  	/**
70  	 * Create a new ID, using a BigDecimal input. Uses {@link BigDecimal#toPlainString()} to generate the string representation.
71  	 */
72  	public IdDt(BigDecimal thePid) {
73  		if (thePid != null) {
74  			setValue(toPlainStringWithNpeThrowIfNeeded(thePid));
75  		} else {
76  			setValue(null);
77  		}
78  	}
79  
80  	/**
81  	 * Create a new ID using a long
82  	 */
83  	public IdDt(long theId) {
84  		setValue(Long.toString(theId));
85  	}
86  
87  	/**
88  	 * Create a new ID using a string. This String may contain a simple ID (e.g. "1234") or it may contain a complete URL (http://example.com/fhir/Patient/1234).
89  	 * <p>
90  	 * <p>
91  	 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
92  	 * limit of 36 characters.
93  	 * </p>
94  	 * <p>
95  	 * regex: [a-z0-9\-\.]{1,36}
96  	 * </p>
97  	 */
98  	@SimpleSetter
99  	public IdDt(@SimpleSetter.Parameter(name = "theId") String theValue) {
100 		setValue(theValue);
101 	}
102 
103 	/**
104 	 * Constructor
105 	 *
106 	 * @param theResourceType The resource type (e.g. "Patient")
107 	 * @param theIdPart       The ID (e.g. "123")
108 	 */
109 	public IdDt(String theResourceType, BigDecimal theIdPart) {
110 		this(theResourceType, toPlainStringWithNpeThrowIfNeeded(theIdPart));
111 	}
112 
113 	/**
114 	 * Constructor
115 	 *
116 	 * @param theResourceType The resource type (e.g. "Patient")
117 	 * @param theIdPart       The ID (e.g. "123")
118 	 */
119 	public IdDt(String theResourceType, Long theIdPart) {
120 		this(theResourceType, toPlainStringWithNpeThrowIfNeeded(theIdPart));
121 	}
122 
123 	/**
124 	 * Constructor
125 	 *
126 	 * @param theResourceType The resource type (e.g. "Patient")
127 	 * @param theId           The ID (e.g. "123")
128 	 */
129 	public IdDt(String theResourceType, String theId) {
130 		this(theResourceType, theId, null);
131 	}
132 
133 	/**
134 	 * Constructor
135 	 *
136 	 * @param theResourceType The resource type (e.g. "Patient")
137 	 * @param theId           The ID (e.g. "123")
138 	 * @param theVersionId    The version ID ("e.g. "456")
139 	 */
140 	public IdDt(String theResourceType, String theId, String theVersionId) {
141 		this(null, theResourceType, theId, theVersionId);
142 	}
143 
144 	/**
145 	 * Constructor
146 	 *
147 	 * @param theBaseUrl      The server base URL (e.g. "http://example.com/fhir")
148 	 * @param theResourceType The resource type (e.g. "Patient")
149 	 * @param theId           The ID (e.g. "123")
150 	 * @param theVersionId    The version ID ("e.g. "456")
151 	 */
152 	public IdDt(String theBaseUrl, String theResourceType, String theId, String theVersionId) {
153 		myBaseUrl = theBaseUrl;
154 		myResourceType = theResourceType;
155 		myUnqualifiedId = theId;
156 		myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionId, null);
157 		myHaveComponentParts = true;
158 		if (isBlank(myBaseUrl) && isBlank(myResourceType) && isBlank(myUnqualifiedId) && isBlank(myUnqualifiedVersionId)) {
159 			myHaveComponentParts = false;
160 		}
161 	}
162 
163 	/**
164 	 * Creates an ID based on a given URL
165 	 */
166 	public IdDt(UriDt theUrl) {
167 		setValue(theUrl.getValueAsString());
168 	}
169 
170 	@Override
171 	public void applyTo(IBaseResource theResouce) {
172 		if (theResouce == null) {
173 			throw new NullPointerException("theResource can not be null");
174 		} else if (theResouce instanceof IResource) {
175 			((IResource) theResouce).setId(new IdDt(getValue()));
176 		} else if (theResouce instanceof IAnyResource) {
177 			((IAnyResource) theResouce).setId(getValue());
178 		} else {
179 			throw new IllegalArgumentException("Unknown resource class type, does not implement IResource or extend Resource");
180 		}
181 	}
182 
183 	/**
184 	 * @deprecated Use {@link #getIdPartAsBigDecimal()} instead (this method was deprocated because its name is ambiguous)
185 	 */
186 	@Deprecated
187 	public BigDecimal asBigDecimal() {
188 		return getIdPartAsBigDecimal();
189 	}
190 
191 	@Override
192 	public boolean equals(Object theArg0) {
193 		if (!(theArg0 instanceof IdDt)) {
194 			return false;
195 		}
196 		IdDt id = (IdDt) theArg0;
197 		return StringUtils.equals(getValueAsString(), id.getValueAsString());
198 	}
199 
200 	/**
201 	 * Returns true if this IdDt matches the given IdDt in terms of resource type and ID, but ignores the URL base
202 	 */
203 	@SuppressWarnings("deprecation")
204 	public boolean equalsIgnoreBase(IdDt theId) {
205 		if (theId == null) {
206 			return false;
207 		}
208 		if (theId.isEmpty()) {
209 			return isEmpty();
210 		}
211 		return ObjectUtils.equals(getResourceType(), theId.getResourceType()) && ObjectUtils.equals(getIdPart(), theId.getIdPart()) && ObjectUtils.equals(getVersionIdPart(), theId.getVersionIdPart());
212 	}
213 
214 	/**
215 	 * Returns the portion of this resource ID which corresponds to the server base URL. For example given the resource ID <code>http://example.com/fhir/Patient/123</code> the base URL would be
216 	 * <code>http://example.com/fhir</code>.
217 	 * <p>
218 	 * This method may return null if the ID contains no base (e.g. "Patient/123")
219 	 * </p>
220 	 */
221 	@Override
222 	public String getBaseUrl() {
223 		return myBaseUrl;
224 	}
225 
226 	/**
227 	 * Returns only the logical ID part of this ID. For example, given the ID "http://example,.com/fhir/Patient/123/_history/456", this method would return "123".
228 	 */
229 	@Override
230 	public String getIdPart() {
231 		return myUnqualifiedId;
232 	}
233 
234 	/**
235 	 * Returns the unqualified portion of this ID as a big decimal, or <code>null</code> if the value is null
236 	 *
237 	 * @throws NumberFormatException If the value is not a valid BigDecimal
238 	 */
239 	public BigDecimal getIdPartAsBigDecimal() {
240 		String val = getIdPart();
241 		if (isBlank(val)) {
242 			return null;
243 		}
244 		return new BigDecimal(val);
245 	}
246 
247 	/**
248 	 * Returns the unqualified portion of this ID as a {@link Long}, or <code>null</code> if the value is null
249 	 *
250 	 * @throws NumberFormatException If the value is not a valid Long
251 	 */
252 	@Override
253 	public Long getIdPartAsLong() {
254 		String val = getIdPart();
255 		if (isBlank(val)) {
256 			return null;
257 		}
258 		return Long.parseLong(val);
259 	}
260 
261 	@Override
262 	public String getResourceType() {
263 		return myResourceType;
264 	}
265 
266 	/**
267 	 * Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a simple ID. Use {@link #getIdPart()} to get just the ID portion.
268 	 *
269 	 * @see #getIdPart()
270 	 */
271 	@Override
272 	public String getValue() {
273 		if (super.getValue() == null && myHaveComponentParts) {
274 
275 			if (isLocal() || isUrn()) {
276 				return myUnqualifiedId;
277 			}
278 
279 			StringBuilder b = new StringBuilder();
280 			if (isNotBlank(myBaseUrl)) {
281 				b.append(myBaseUrl);
282 				if (myBaseUrl.charAt(myBaseUrl.length() - 1) != '/') {
283 					b.append('/');
284 				}
285 			}
286 
287 			if (isNotBlank(myResourceType)) {
288 				b.append(myResourceType);
289 			}
290 
291 			if (b.length() > 0 && isNotBlank(myUnqualifiedId)) {
292 				b.append('/');
293 			}
294 
295 			if (isNotBlank(myUnqualifiedId)) {
296 				b.append(myUnqualifiedId);
297 			} else if (isNotBlank(myUnqualifiedVersionId)) {
298 				b.append('/');
299 			}
300 
301 			if (isNotBlank(myUnqualifiedVersionId)) {
302 				b.append('/');
303 				b.append(Constants.PARAM_HISTORY);
304 				b.append('/');
305 				b.append(myUnqualifiedVersionId);
306 			}
307 			String value = b.toString();
308 			super.setValue(value);
309 		}
310 		return super.getValue();
311 	}
312 
313 	/**
314 	 * Set the value
315 	 * <p>
316 	 * <p>
317 	 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
318 	 * limit of 36 characters.
319 	 * </p>
320 	 * <p>
321 	 * regex: [a-z0-9\-\.]{1,36}
322 	 * </p>
323 	 */
324 	@Override
325 	public IdDt setValue(String theValue) throws DataFormatException {
326 		// TODO: add validation
327 		super.setValue(theValue);
328 		myHaveComponentParts = false;
329 
330 		if (StringUtils.isBlank(theValue)) {
331 			myBaseUrl = null;
332 			super.setValue(null);
333 			myUnqualifiedId = null;
334 			myUnqualifiedVersionId = null;
335 			myResourceType = null;
336 		} else if (theValue.charAt(0) == '#' && theValue.length() > 1) {
337 			super.setValue(theValue);
338 			myBaseUrl = null;
339 			myUnqualifiedId = theValue;
340 			myUnqualifiedVersionId = null;
341 			myResourceType = null;
342 			myHaveComponentParts = true;
343 		} else if (theValue.startsWith("urn:")) {
344 			myBaseUrl = null;
345 			myUnqualifiedId = theValue;
346 			myUnqualifiedVersionId = null;
347 			myResourceType = null;
348 			myHaveComponentParts = true;
349 		} else {
350 			int vidIndex = theValue.indexOf("/_history/");
351 			int idIndex;
352 			if (vidIndex != -1) {
353 				myUnqualifiedVersionId = theValue.substring(vidIndex + "/_history/".length());
354 				idIndex = theValue.lastIndexOf('/', vidIndex - 1);
355 				myUnqualifiedId = theValue.substring(idIndex + 1, vidIndex);
356 			} else {
357 				idIndex = theValue.lastIndexOf('/');
358 				myUnqualifiedId = theValue.substring(idIndex + 1);
359 				myUnqualifiedVersionId = null;
360 			}
361 
362 			myBaseUrl = null;
363 			if (idIndex <= 0) {
364 				myResourceType = null;
365 			} else {
366 				int typeIndex = theValue.lastIndexOf('/', idIndex - 1);
367 				if (typeIndex == -1) {
368 					myResourceType = theValue.substring(0, idIndex);
369 				} else {
370 					if (typeIndex > 0 && '/' == theValue.charAt(typeIndex - 1)) {
371 						typeIndex = theValue.indexOf('/', typeIndex + 1);
372 					}
373 					if (typeIndex >= idIndex) {
374 						// e.g. http://example.org/foo
375 						// 'foo' was the id but we're making that the resource type. Nullify the id part because we don't have an id.
376 						// Also set null value to the super.setValue() and enable myHaveComponentParts so it forces getValue() to properly
377 						// recreate the url
378 						myResourceType = myUnqualifiedId;
379 						myUnqualifiedId = null;
380 						super.setValue(null);
381 						myHaveComponentParts = true;
382 					} else {
383 						myResourceType = theValue.substring(typeIndex + 1, idIndex);
384 					}
385 
386 					if (typeIndex > 4) {
387 						myBaseUrl = theValue.substring(0, typeIndex);
388 					}
389 
390 				}
391 			}
392 
393 		}
394 		return this;
395 	}
396 
397 	@Override
398 	public String getValueAsString() {
399 		return getValue();
400 	}
401 
402 	/**
403 	 * Set the value
404 	 * <p>
405 	 * <p>
406 	 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
407 	 * limit of 36 characters.
408 	 * </p>
409 	 * <p>
410 	 * regex: [a-z0-9\-\.]{1,36}
411 	 * </p>
412 	 */
413 	@Override
414 	public void setValueAsString(String theValue) throws DataFormatException {
415 		setValue(theValue);
416 	}
417 
418 	@Override
419 	public String getVersionIdPart() {
420 		return myUnqualifiedVersionId;
421 	}
422 
423 	@Override
424 	public Long getVersionIdPartAsLong() {
425 		if (!hasVersionIdPart()) {
426 			return null;
427 		}
428 		return Long.parseLong(getVersionIdPart());
429 	}
430 
431 	/**
432 	 * Returns true if this ID has a base url
433 	 *
434 	 * @see #getBaseUrl()
435 	 */
436 	@Override
437 	public boolean hasBaseUrl() {
438 		return isNotBlank(myBaseUrl);
439 	}
440 
441 	@Override
442 	public boolean hasIdPart() {
443 		return isNotBlank(getIdPart());
444 	}
445 
446 	@Override
447 	public boolean hasResourceType() {
448 		return isNotBlank(myResourceType);
449 	}
450 
451 	@Override
452 	public boolean hasVersionIdPart() {
453 		return isNotBlank(getVersionIdPart());
454 	}
455 
456 	@Override
457 	public int hashCode() {
458 		HashCodeBuilder b = new HashCodeBuilder();
459 		b.append(getValueAsString());
460 		return b.toHashCode();
461 	}
462 
463 	/**
464 	 * Returns <code>true</code> if this ID contains an absolute URL (in other words, a URL starting with "http://" or "https://"
465 	 */
466 	@Override
467 	public boolean isAbsolute() {
468 		if (StringUtils.isBlank(getValue())) {
469 			return false;
470 		}
471 		return UrlUtil.isAbsolute(getValue());
472 	}
473 
474 	@Override
475 	public boolean isEmpty() {
476 		return super.isBaseEmpty() && isBlank(getValue());
477 	}
478 
479 	@Override
480 	public boolean isIdPartValid() {
481 		String id = getIdPart();
482 		if (StringUtils.isBlank(id)) {
483 			return false;
484 		}
485 		if (id.length() > 64) {
486 			return false;
487 		}
488 		for (int i = 0; i < id.length(); i++) {
489 			char nextChar = id.charAt(i);
490 			if (nextChar >= 'a' && nextChar <= 'z') {
491 				continue;
492 			}
493 			if (nextChar >= 'A' && nextChar <= 'Z') {
494 				continue;
495 			}
496 			if (nextChar >= '0' && nextChar <= '9') {
497 				continue;
498 			}
499 			if (nextChar == '-' || nextChar == '.') {
500 				continue;
501 			}
502 			return false;
503 		}
504 		return true;
505 	}
506 
507 	@Override
508 	public boolean isIdPartValidLong() {
509 		return isValidLong(getIdPart());
510 	}
511 
512 	/**
513 	 * Returns <code>true</code> if the ID is a local reference (in other words,
514 	 * it begins with the '#' character)
515 	 */
516 	@Override
517 	public boolean isLocal() {
518 		return defaultString(myUnqualifiedId).startsWith("#");
519 	}
520 
521 	private boolean isUrn() {
522 		return defaultString(myUnqualifiedId).startsWith("urn:");
523 	}
524 
525 	@Override
526 	public boolean isVersionIdPartValidLong() {
527 		return isValidLong(getVersionIdPart());
528 	}
529 
530 	/**
531 	 * Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API.
532 	 *
533 	 * @deprecated
534 	 */
535 	@Deprecated //override deprecated method
536 	@Override
537 	public void setId(IdDt theId) {
538 		setValue(theId.getValue());
539 	}
540 
541 	@Override
542 	public IIdType setParts(String theBaseUrl, String theResourceType, String theIdPart, String theVersionIdPart) {
543 		if (isNotBlank(theVersionIdPart)) {
544 			Validate.notBlank(theResourceType, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated");
545 			Validate.notBlank(theIdPart, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated");
546 		}
547 		if (isNotBlank(theBaseUrl) && isNotBlank(theIdPart)) {
548 			Validate.notBlank(theResourceType, "If theBaseUrl is populated and theIdPart is populated, theResourceType must be populated");
549 		}
550 
551 		setValue(null);
552 
553 		myBaseUrl = theBaseUrl;
554 		myResourceType = theResourceType;
555 		myUnqualifiedId = theIdPart;
556 		myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionIdPart, null);
557 		myHaveComponentParts = true;
558 
559 		return this;
560 	}
561 
562 	@Override
563 	public String toString() {
564 		return getValue();
565 	}
566 
567 	/**
568 	 * Returns a new IdDt containing this IdDt's values but with no server base URL if one is present in this IdDt. For example, if this IdDt contains the ID "http://foo/Patient/1", this method will
569 	 * return a new IdDt containing ID "Patient/1".
570 	 */
571 	@Override
572 	public IdDt toUnqualified() {
573 		if (isLocal() || isUrn()) {
574 			return new IdDt(getValueAsString());
575 		}
576 		return new IdDt(getResourceType(), getIdPart(), getVersionIdPart());
577 	}
578 
579 	@Override
580 	public IdDt toUnqualifiedVersionless() {
581 		if (isLocal() || isUrn()) {
582 			return new IdDt(getValueAsString());
583 		}
584 		return new IdDt(getResourceType(), getIdPart());
585 	}
586 
587 	@Override
588 	public IdDt toVersionless() {
589 		if (isLocal() || isUrn()) {
590 			return new IdDt(getValueAsString());
591 		}
592 		return new IdDt(getBaseUrl(), getResourceType(), getIdPart(), null);
593 	}
594 
595 	@Override
596 	public IdDt withResourceType(String theResourceName) {
597 		if (isLocal() || isUrn()) {
598 			return new IdDt(getValueAsString());
599 		}
600 		return new IdDt(theResourceName, getIdPart(), getVersionIdPart());
601 	}
602 
603 	/**
604 	 * Returns a view of this ID as a fully qualified URL, given a server base and resource name (which will only be used if the ID does not already contain those respective parts). Essentially,
605 	 * because IdDt can contain either a complete URL or a partial one (or even jut a simple ID), this method may be used to translate into a complete URL.
606 	 *
607 	 * @param theServerBase   The server base (e.g. "http://example.com/fhir")
608 	 * @param theResourceType The resource name (e.g. "Patient")
609 	 * @return A fully qualified URL for this ID (e.g. "http://example.com/fhir/Patient/1")
610 	 */
611 	@Override
612 	public IdDt withServerBase(String theServerBase, String theResourceType) {
613 		if (isLocal() || isUrn()) {
614 			return new IdDt(getValueAsString());
615 		}
616 		return new IdDt(theServerBase, theResourceType, getIdPart(), getVersionIdPart());
617 	}
618 
619 	/**
620 	 * Creates a new instance of this ID which is identical, but refers to the specific version of this resource ID noted by theVersion.
621 	 *
622 	 * @param theVersion The actual version string, e.g. "1". If theVersion is blank or null, returns the same as {@link #toVersionless()}}
623 	 * @return A new instance of IdDt which is identical, but refers to the specific version of this resource ID noted by theVersion.
624 	 */
625 	@Override
626 	public IdDt withVersion(String theVersion) {
627 		if (isBlank(theVersion)) {
628 			return toVersionless();
629 		}
630 
631 		if (isLocal() || isUrn()) {
632 			return new IdDt(getValueAsString());
633 		}
634 
635 		String existingValue = getValue();
636 
637 		int i = existingValue.indexOf(Constants.PARAM_HISTORY);
638 		String value;
639 		if (i > 1) {
640 			value = existingValue.substring(0, i - 1);
641 		} else {
642 			value = existingValue;
643 		}
644 
645 		return new IdDt(value + '/' + Constants.PARAM_HISTORY + '/' + theVersion);
646 	}
647 
648 	private static boolean isValidLong(String id) {
649 		if (StringUtils.isBlank(id)) {
650 			return false;
651 		}
652 		for (int i = 0; i < id.length(); i++) {
653 			if (Character.isDigit(id.charAt(i)) == false) {
654 				return false;
655 			}
656 		}
657 		return true;
658 	}
659 
660 	/**
661 	 * Construct a new ID with with form "urn:uuid:[UUID]" where [UUID] is a new, randomly
662 	 * created UUID generated by {@link UUID#randomUUID()}
663 	 */
664 	public static IdDt newRandomUuid() {
665 		return new IdDt("urn:uuid:" + UUID.randomUUID().toString());
666 	}
667 
668 	/**
669 	 * Retrieves the ID from the given resource instance
670 	 */
671 	public static IdDt of(IBaseResource theResouce) {
672 		if (theResouce == null) {
673 			throw new NullPointerException("theResource can not be null");
674 		}
675 		IIdType retVal = theResouce.getIdElement();
676 		if (retVal == null) {
677 			return null;
678 		} else if (retVal instanceof IdDt) {
679 			return (IdDt) retVal;
680 		} else {
681 			return new IdDt(retVal.getValue());
682 		}
683 	}
684 
685 	private static String toPlainStringWithNpeThrowIfNeeded(BigDecimal theIdPart) {
686 		if (theIdPart == null) {
687 			throw new NullPointerException("BigDecimal ID can not be null");
688 		}
689 		return theIdPart.toPlainString();
690 	}
691 
692 	private static String toPlainStringWithNpeThrowIfNeeded(Long theIdPart) {
693 		if (theIdPart == null) {
694 			throw new NullPointerException("Long ID can not be null");
695 		}
696 		return theIdPart.toString();
697 	}
698 
699 }