1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.api.ldap.model.name;
21
22
23 import java.io.Externalizable;
24 import java.io.IOException;
25 import java.io.ObjectInput;
26 import java.io.ObjectOutput;
27 import java.util.Arrays;
28
29 import org.apache.directory.api.i18n.I18n;
30 import org.apache.directory.api.ldap.model.entry.BinaryValue;
31 import org.apache.directory.api.ldap.model.entry.StringValue;
32 import org.apache.directory.api.ldap.model.entry.Value;
33 import org.apache.directory.api.ldap.model.exception.LdapException;
34 import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
35 import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
36 import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
37 import org.apache.directory.api.ldap.model.schema.AttributeType;
38 import org.apache.directory.api.ldap.model.schema.LdapComparator;
39 import org.apache.directory.api.ldap.model.schema.MatchingRule;
40 import org.apache.directory.api.ldap.model.schema.SchemaManager;
41 import org.apache.directory.api.util.Serialize;
42 import org.apache.directory.api.util.Strings;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class Ava implements Externalizable, Cloneable, Comparable<Ava>
61 {
62
63
64
65
66
67
68
69 private static final long serialVersionUID = 1L;
70
71
72 private static final Logger LOG = LoggerFactory.getLogger( Ava.class );
73
74
75 private String normType;
76
77
78 private String upType;
79
80
81 private Value<?> value;
82
83
84 private String upName;
85
86
87 private AttributeType attributeType;
88
89
90 private SchemaManager schemaManager;
91
92
93 private volatile int h;
94
95
96
97
98
99 public Ava()
100 {
101 this( null );
102 }
103
104
105
106
107
108
109
110 public Ava( SchemaManager schemaManager )
111 {
112 normType = null;
113 upType = null;
114 value = null;
115 upName = "";
116 this.schemaManager = schemaManager;
117 this.attributeType = null;
118 }
119
120
121
122
123
124
125
126
127
128
129
130
131
132 public Ava( String upType, byte[] upValue ) throws LdapInvalidDnException
133 {
134 this( null, upType, upValue );
135 }
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151 public Ava( SchemaManager schemaManager, String upType, byte[] upValue ) throws LdapInvalidDnException
152 {
153 if ( schemaManager != null )
154 {
155 this.schemaManager = schemaManager;
156
157 try
158 {
159 attributeType = schemaManager.lookupAttributeTypeRegistry( upType );
160 }
161 catch ( LdapException le )
162 {
163 String message = I18n.err( I18n.ERR_04188 );
164 LOG.error( message );
165 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
166 }
167
168 try
169 {
170 createAva( schemaManager, upType, new BinaryValue( attributeType, upValue ) );
171 }
172 catch ( LdapInvalidAttributeValueException liave )
173 {
174 String message = I18n.err( I18n.ERR_04188 );
175 LOG.error( message );
176 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, liave );
177 }
178 }
179 else
180 {
181 createAva( upType, new BinaryValue( upValue ) );
182 }
183 }
184
185
186
187
188
189
190
191
192
193
194
195
196
197 public Ava( String upType, String upValue ) throws LdapInvalidDnException
198 {
199 this( null, upType, upValue );
200 }
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215 public Ava( SchemaManager schemaManager, String upType, String upValue ) throws LdapInvalidDnException
216 {
217 if ( schemaManager != null )
218 {
219 this.schemaManager = schemaManager;
220
221 try
222 {
223 attributeType = schemaManager.lookupAttributeTypeRegistry( upType );
224 }
225 catch ( LdapException le )
226 {
227 String message = I18n.err( I18n.ERR_04188 );
228 LOG.error( message );
229 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
230 }
231
232 try
233 {
234 createAva( schemaManager, upType, new StringValue( attributeType, upValue ) );
235 }
236 catch ( LdapInvalidAttributeValueException liave )
237 {
238 String message = I18n.err( I18n.ERR_04188 );
239 LOG.error( message );
240 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, liave );
241 }
242 }
243 else
244 {
245 createAva( upType, new StringValue( upValue ) );
246 }
247 }
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268 Ava( SchemaManager schemaManager, String upType, String normType, Value<?> value )
269 throws LdapInvalidDnException
270 {
271 this.upType = upType;
272 this.normType = normType;
273 this.value = value;
274 upName = this.upType + '=' + ( this.value == null ? "" : this.value.getString() );
275
276 if ( schemaManager != null )
277 {
278 apply( schemaManager );
279 }
280
281 hashCode();
282 }
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303 Ava( String upType, String normType, Value<?> value, String upName )
304 throws LdapInvalidDnException
305 {
306 this( null, upType, normType, value, upName );
307 }
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329 Ava( AttributeType attributeType, String upType, String normType, Value<?> value, String upName )
330 throws LdapInvalidDnException
331 {
332 this.attributeType = attributeType;
333 String upTypeTrimmed = Strings.trim( upType );
334 String normTypeTrimmed = Strings.trim( normType );
335
336 if ( Strings.isEmpty( upTypeTrimmed ) )
337 {
338 if ( Strings.isEmpty( normTypeTrimmed ) )
339 {
340 String message = I18n.err( I18n.ERR_04188 );
341 LOG.error( message );
342 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message );
343 }
344 else
345 {
346
347 this.normType = Strings.lowerCaseAscii( normTypeTrimmed );
348 this.upType = normType;
349 }
350 }
351 else if ( Strings.isEmpty( normTypeTrimmed ) )
352 {
353
354 this.normType = Strings.lowerCaseAscii( upTypeTrimmed );
355 this.upType = upType;
356 }
357 else
358 {
359 this.normType = Strings.lowerCaseAscii( normTypeTrimmed );
360 this.upType = upType;
361 }
362
363 this.value = value;
364 this.upName = upName;
365 hashCode();
366 }
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382 private void createAva( SchemaManager schemaManager, String upType, Value<?> value )
383 throws LdapInvalidDnException
384 {
385 normType = attributeType.getOid();
386 this.upType = upType;
387 this.value = value;
388 upName = this.upType + '=' + ( value == null ? "" : Rdn.escapeValue( value.getString() ) );
389 hashCode();
390 }
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406 private void createAva( String upType, Value<?> upValue ) throws LdapInvalidDnException
407 {
408 String upTypeTrimmed = Strings.trim( upType );
409 String normTypeTrimmed = Strings.trim( normType );
410
411 if ( Strings.isEmpty( upTypeTrimmed ) )
412 {
413 if ( Strings.isEmpty( normTypeTrimmed ) )
414 {
415 String message = I18n.err( I18n.ERR_04188 );
416 LOG.error( message );
417 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message );
418 }
419 else
420 {
421
422 this.normType = Strings.lowerCaseAscii( normTypeTrimmed );
423 this.upType = normType;
424 }
425 }
426 else if ( Strings.isEmpty( normTypeTrimmed ) )
427 {
428
429 this.normType = Strings.lowerCaseAscii( upTypeTrimmed );
430 this.upType = upType;
431 }
432 else
433 {
434 this.normType = Strings.lowerCaseAscii( normTypeTrimmed );
435 this.upType = upType;
436
437 }
438
439 value = upValue;
440
441 upName = this.upType + '=' + ( value == null ? "" : Rdn.escapeValue( value.getString() ) );
442 hashCode();
443 }
444
445
446
447
448
449
450
451
452
453
454
455 public void apply( SchemaManager schemaManager ) throws LdapInvalidDnException
456 {
457 if ( schemaManager != null )
458 {
459 this.schemaManager = schemaManager;
460
461 AttributeType tmpAttributeType = null;
462
463 try
464 {
465 tmpAttributeType = schemaManager.lookupAttributeTypeRegistry( normType );
466 }
467 catch ( LdapException le )
468 {
469 if ( schemaManager.isRelaxed() )
470 {
471
472 return;
473 }
474 else
475 {
476 String message = I18n.err( I18n.ERR_04188 );
477 LOG.error( message );
478 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
479 }
480 }
481
482 if ( this.attributeType == tmpAttributeType )
483 {
484
485 return;
486 }
487 else
488 {
489 this.attributeType = tmpAttributeType;
490 }
491
492 normType = tmpAttributeType.getOid();
493
494 try
495 {
496 this.value.apply( tmpAttributeType );
497 }
498 catch ( LdapException le )
499 {
500 String message = I18n.err( I18n.ERR_04188 );
501 LOG.error( message );
502 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le );
503 }
504
505 hashCode();
506 }
507 }
508
509
510
511
512
513
514
515 public String getNormType()
516 {
517 return normType;
518 }
519
520
521
522
523
524
525
526 public String getType()
527 {
528 return upType;
529 }
530
531
532
533
534
535
536
537 public Value<?> getValue()
538 {
539 return value.clone();
540 }
541
542
543
544
545
546
547
548 public String getNormName()
549 {
550 return normalize();
551 }
552
553
554
555
556
557
558
559 public String getName()
560 {
561 return upName;
562 }
563
564
565
566
567
568
569
570 @Override
571 public Ava clone()
572 {
573 try
574 {
575 Ava clone = ( Ava ) super.clone();
576 clone.value = value.clone();
577
578 return clone;
579 }
580 catch ( CloneNotSupportedException cnse )
581 {
582 throw new Error( "Assertion failure", cnse );
583 }
584 }
585
586
587
588
589
590
591
592
593
594
595
596 public String normalize()
597 {
598 if ( value.isHumanReadable() )
599 {
600
601 StringBuilder sb = new StringBuilder();
602
603
604 sb.append( normType ).append( '=' );
605
606 String normalizedValue = ( String ) value.getNormValue();
607
608 if ( ( normalizedValue != null ) && ( normalizedValue.length() > 0 ) )
609 {
610 sb.append( Rdn.escapeValue( normalizedValue ) );
611 }
612
613 return sb.toString();
614 }
615 else
616 {
617 return normType + "=#"
618 + Strings.dumpHexPairs( value.getBytes() );
619 }
620 }
621
622
623
624
625
626
627
628
629 @Override
630 public int hashCode()
631 {
632 if ( h == 0 )
633 {
634 h = 37;
635
636 h = h * 17 + ( normType != null ? normType.hashCode() : 0 );
637 h = h * 17 + ( value != null ? value.hashCode() : 0 );
638 }
639
640 return h;
641 }
642
643
644
645
646
647 @Override
648 public boolean equals( Object obj )
649 {
650 if ( this == obj )
651 {
652 return true;
653 }
654
655 if ( !( obj instanceof Ava ) )
656 {
657 return false;
658 }
659
660 Ava instance = ( Ava ) obj;
661
662
663 if ( normType == null )
664 {
665 if ( instance.normType != null )
666 {
667 return false;
668 }
669 }
670 else
671 {
672 if ( !normType.equals( instance.normType ) )
673 {
674 return false;
675 }
676 }
677
678
679 if ( value.isNull() )
680 {
681 return instance.value.isNull();
682 }
683 else
684 {
685 if ( schemaManager != null )
686 {
687 MatchingRule equalityMatchingRule = attributeType.getEquality();
688
689 if ( equalityMatchingRule != null )
690 {
691 return equalityMatchingRule.getLdapComparator().compare( value.getValue(),
692 instance.value.getValue() ) == 0;
693 }
694 else
695 {
696
697 if ( value instanceof BinaryValue )
698 {
699 return Arrays.equals( value.getBytes(), instance.value.getBytes() );
700 }
701 else
702 {
703 return value.getString().equals( instance.value.getString() );
704 }
705 }
706 }
707 else
708 {
709 return value.equals( instance.value );
710 }
711 }
712 }
713
714
715
716
717
718
719
720
721
722
723 public int serialize( byte[] buffer, int pos ) throws IOException
724 {
725 if ( Strings.isEmpty( upName )
726 || Strings.isEmpty( upType )
727 || Strings.isEmpty( normType )
728 || ( value.isNull() ) )
729 {
730 String message = "Cannot serialize an wrong ATAV, ";
731
732 if ( Strings.isEmpty( upName ) )
733 {
734 message += "the upName should not be null or empty";
735 }
736 else if ( Strings.isEmpty( upType ) )
737 {
738 message += "the upType should not be null or empty";
739 }
740 else if ( Strings.isEmpty( normType ) )
741 {
742 message += "the normType should not be null or empty";
743 }
744 else if ( value.isNull() )
745 {
746 message += "the value should not be null";
747 }
748
749 LOG.error( message );
750 throw new IOException( message );
751 }
752
753 int length = 0;
754
755
756 byte[] upNameBytes = null;
757
758 if ( upName != null )
759 {
760 upNameBytes = Strings.getBytesUtf8( upName );
761 length += 1 + 4 + upNameBytes.length;
762 }
763
764
765 byte[] upTypeBytes = null;
766
767 if ( upType != null )
768 {
769 upTypeBytes = Strings.getBytesUtf8( upType );
770 length += 1 + 4 + upTypeBytes.length;
771 }
772
773
774 byte[] normTypeBytes = null;
775
776 if ( normType != null )
777 {
778 normTypeBytes = Strings.getBytesUtf8( normType );
779 length += 1 + 4 + normTypeBytes.length;
780 }
781
782
783 length++;
784
785
786 length += 4;
787
788
789 if ( buffer.length - pos < length )
790 {
791 throw new ArrayIndexOutOfBoundsException();
792 }
793
794
795 if ( upName != null )
796 {
797 buffer[pos++] = Serialize.TRUE;
798 pos = Serialize.serialize( upNameBytes, buffer, pos );
799 }
800 else
801 {
802 buffer[pos++] = Serialize.FALSE;
803 }
804
805
806 if ( upType != null )
807 {
808 buffer[pos++] = Serialize.TRUE;
809 pos = Serialize.serialize( upTypeBytes, buffer, pos );
810 }
811 else
812 {
813 buffer[pos++] = Serialize.FALSE;
814 }
815
816
817 if ( normType != null )
818 {
819 buffer[pos++] = Serialize.TRUE;
820 pos = Serialize.serialize( normTypeBytes, buffer, pos );
821 }
822 else
823 {
824 buffer[pos++] = Serialize.FALSE;
825 }
826
827
828 if ( value.isHumanReadable() )
829 {
830 buffer[pos++] = Serialize.TRUE;
831 }
832 else
833 {
834 buffer[pos++] = Serialize.FALSE;
835 }
836
837
838 if ( value.isHumanReadable() )
839 {
840 pos = ( ( StringValue ) value ).serialize( buffer, pos );
841 }
842
843
844 pos = Serialize.serialize( h, buffer, pos );
845
846 return pos;
847 }
848
849
850
851
852
853
854
855
856
857
858
859 public int deserialize( byte[] buffer, int pos ) throws IOException, LdapInvalidAttributeValueException
860 {
861 if ( ( pos < 0 ) || ( pos >= buffer.length ) )
862 {
863 throw new ArrayIndexOutOfBoundsException();
864 }
865
866
867 boolean hasUpName = Serialize.deserializeBoolean( buffer, pos );
868 pos++;
869
870 if ( hasUpName )
871 {
872 byte[] wrappedValueBytes = Serialize.deserializeBytes( buffer, pos );
873 pos += 4 + wrappedValueBytes.length;
874 upName = Strings.utf8ToString( wrappedValueBytes );
875 }
876
877
878 boolean hasUpType = Serialize.deserializeBoolean( buffer, pos );
879 pos++;
880
881 if ( hasUpType )
882 {
883 byte[] upTypeBytes = Serialize.deserializeBytes( buffer, pos );
884 pos += 4 + upTypeBytes.length;
885 upType = Strings.utf8ToString( upTypeBytes );
886 }
887
888
889 boolean hasNormType = Serialize.deserializeBoolean( buffer, pos );
890 pos++;
891
892 if ( hasNormType )
893 {
894 byte[] normTypeBytes = Serialize.deserializeBytes( buffer, pos );
895 pos += 4 + normTypeBytes.length;
896 normType = Strings.utf8ToString( normTypeBytes );
897 }
898
899
900 if ( schemaManager != null )
901 {
902 if ( !Strings.isEmpty( upType ) )
903 {
904 attributeType = schemaManager.getAttributeType( upType );
905 }
906 else
907 {
908 attributeType = schemaManager.getAttributeType( normType );
909 }
910 }
911
912
913 boolean isHR = Serialize.deserializeBoolean( buffer, pos );
914 pos++;
915
916 if ( isHR )
917 {
918
919 value = new StringValue( attributeType );
920 pos = ( ( StringValue ) value ).deserialize( buffer, pos );
921 }
922
923
924 h = Serialize.deserializeInt( buffer, pos );
925 pos += 4;
926
927 return pos;
928 }
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977 @Override
978 public void writeExternal( ObjectOutput out ) throws IOException
979 {
980 if ( Strings.isEmpty( upName )
981 || Strings.isEmpty( upType )
982 || Strings.isEmpty( normType )
983 || ( value.isNull() ) )
984 {
985 String message = "Cannot serialize a wrong ATAV, ";
986
987 if ( Strings.isEmpty( upName ) )
988 {
989 message += "the upName should not be null or empty";
990 }
991 else if ( Strings.isEmpty( upType ) )
992 {
993 message += "the upType should not be null or empty";
994 }
995 else if ( Strings.isEmpty( normType ) )
996 {
997 message += "the normType should not be null or empty";
998 }
999 else if ( value.isNull() )
1000 {
1001 message += "the value should not be null";
1002 }
1003
1004 LOG.error( message );
1005 throw new IOException( message );
1006 }
1007
1008 if ( upName != null )
1009 {
1010 out.writeBoolean( true );
1011 out.writeUTF( upName );
1012 }
1013 else
1014 {
1015 out.writeBoolean( false );
1016 }
1017
1018 if ( upType != null )
1019 {
1020 out.writeBoolean( true );
1021 out.writeUTF( upType );
1022 }
1023 else
1024 {
1025 out.writeBoolean( false );
1026 }
1027
1028 if ( normType != null )
1029 {
1030 out.writeBoolean( true );
1031 out.writeUTF( normType );
1032 }
1033 else
1034 {
1035 out.writeBoolean( false );
1036 }
1037
1038 boolean isHR = value.isHumanReadable();
1039
1040 out.writeBoolean( isHR );
1041
1042 value.writeExternal( out );
1043
1044
1045 out.writeInt( h );
1046
1047 out.flush();
1048 }
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061 @Override
1062 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
1063 {
1064 boolean hasUpName = in.readBoolean();
1065
1066 if ( hasUpName )
1067 {
1068 upName = in.readUTF();
1069 }
1070
1071 boolean hasUpType = in.readBoolean();
1072
1073 if ( hasUpType )
1074 {
1075 upType = in.readUTF();
1076 }
1077
1078 boolean hasNormType = in.readBoolean();
1079
1080 if ( hasNormType )
1081 {
1082 normType = in.readUTF();
1083 }
1084
1085 if ( schemaManager != null )
1086 {
1087 if ( !Strings.isEmpty( upType ) )
1088 {
1089 attributeType = schemaManager.getAttributeType( upType );
1090 }
1091 else
1092 {
1093 attributeType = schemaManager.getAttributeType( normType );
1094 }
1095 }
1096
1097 boolean isHR = in.readBoolean();
1098
1099 if ( isHR )
1100 {
1101 value = StringValue.deserialize( attributeType, in );
1102 }
1103 else
1104 {
1105 value = BinaryValue.deserialize( attributeType, in );
1106 }
1107
1108 h = in.readInt();
1109
1110 if ( schemaManager != null )
1111 {
1112 attributeType = schemaManager.getAttributeType( upType );
1113 }
1114 }
1115
1116
1117
1118
1119
1120
1121
1122 public boolean isSchemaAware()
1123 {
1124 return attributeType != null;
1125 }
1126
1127
1128
1129
1130
1131 public AttributeType getAttributeType()
1132 {
1133 return attributeType;
1134 }
1135
1136
1137 private int compareValues( Ava that )
1138 {
1139 int comp;
1140
1141 if ( value.getNormValue() instanceof String )
1142 {
1143 comp = ( ( String ) value.getNormValue() ).compareTo( ( String ) that.value.getNormValue() );
1144
1145 return comp;
1146 }
1147 else
1148 {
1149 byte[] bytes1 = ( byte[] ) value.getNormValue();
1150 byte[] bytes2 = ( byte[] ) that.value.getNormValue();
1151
1152 for ( int pos = 0; pos < bytes1.length; pos++ )
1153 {
1154 int v1 = bytes1[pos] & 0x00FF;
1155 int v2 = bytes2[pos] & 0x00FF;
1156
1157 if ( v1 > v2 )
1158 {
1159 return 1;
1160 }
1161 else if ( v2 > v1 )
1162 {
1163 return -1;
1164 }
1165 }
1166
1167 return 0;
1168 }
1169
1170 }
1171
1172
1173
1174
1175
1176 @Override
1177 public int compareTo( Ava that )
1178 {
1179 if ( that == null )
1180 {
1181 return 1;
1182 }
1183
1184 int comp;
1185
1186 if ( schemaManager == null )
1187 {
1188
1189 comp = normType.compareTo( that.normType );
1190
1191 if ( comp != 0 )
1192 {
1193 return comp;
1194 }
1195
1196
1197 if ( value == null )
1198 {
1199 if ( that.value == null )
1200 {
1201 return 0;
1202 }
1203 else
1204 {
1205 return -1;
1206 }
1207 }
1208 else
1209 {
1210 if ( that.value == null )
1211 {
1212 return 1;
1213 }
1214 else
1215 {
1216 if ( value instanceof StringValue )
1217 {
1218 comp = ( ( StringValue ) value ).compareTo( ( StringValue ) that.value );
1219
1220 return comp;
1221 }
1222 else
1223 {
1224 comp = ( ( BinaryValue ) value ).compareTo( ( BinaryValue ) that.value );
1225
1226 return comp;
1227 }
1228 }
1229 }
1230 }
1231 else
1232 {
1233 if ( that.schemaManager == null )
1234 {
1235
1236 try
1237 {
1238 that.apply( schemaManager );
1239 }
1240 catch ( LdapInvalidDnException lide )
1241 {
1242 return 1;
1243 }
1244 }
1245
1246
1247 comp = attributeType.getOid().compareTo( that.attributeType.getOid() );
1248
1249 if ( comp != 0 )
1250 {
1251 return comp;
1252 }
1253
1254
1255 MatchingRule orderingMR = attributeType.getOrdering();
1256
1257 if ( orderingMR != null )
1258 {
1259 LdapComparator<Object> comparator = ( LdapComparator<Object> ) orderingMR.getLdapComparator();
1260
1261 if ( comparator != null )
1262 {
1263 comp = comparator.compare( value.getNormValue(), that.value.getNormValue() );
1264
1265 return comp;
1266 }
1267 else
1268 {
1269 comp = compareValues( that );
1270
1271 return comp;
1272 }
1273 }
1274 else
1275 {
1276 comp = compareValues( that );
1277
1278 return comp;
1279 }
1280 }
1281 }
1282
1283
1284
1285
1286
1287
1288
1289 @Override
1290 public String toString()
1291 {
1292 return upName;
1293 }
1294 }