1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44:
45:
46:
55: public final class Bidi
56: {
57:
62: public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = -2;
63:
64:
69: public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = -1;
70:
71:
74: public static final int DIRECTION_LEFT_TO_RIGHT = 0;
75:
76:
79: public static final int DIRECTION_RIGHT_TO_LEFT = 1;
80:
81:
82: private static final int LTOR = 1 << DIRECTION_LEFT_TO_RIGHT;
83: private static final int RTOL = 1 << DIRECTION_RIGHT_TO_LEFT;
84:
85:
86:
87:
88:
89: private char[] text;
90: private int textOffset;
91:
92: private byte[] embeddings;
93: private int embeddingOffset;
94:
95: private int length;
96:
97: private int flags;
98:
99:
100:
101:
102:
103: private int baseEmbedding;
104:
105: private byte[] types;
106:
107: private byte[] levels;
108:
109:
110:
111:
112: private ArrayList<Integer> formatterIndices;
113:
114:
115: private int[] runs;
116:
117:
118:
119: private int resultFlags;
120:
121:
143: public Bidi(AttributedCharacterIterator iter)
144: {
145:
146:
147:
148: Object val = iter.getAttribute(TextAttribute.RUN_DIRECTION);
149: if (val == TextAttribute.RUN_DIRECTION_LTR)
150: this.flags = DIRECTION_LEFT_TO_RIGHT;
151: else if (val == TextAttribute.RUN_DIRECTION_RTL)
152: this.flags = DIRECTION_RIGHT_TO_LEFT;
153: else
154: this.flags = DIRECTION_DEFAULT_LEFT_TO_RIGHT;
155:
156:
157:
158:
159: NumericShaper shaper = null;
160: val = iter.getAttribute(TextAttribute.NUMERIC_SHAPING);
161: if (val instanceof NumericShaper)
162: shaper = (NumericShaper) val;
163:
164: text = new char[iter.getEndIndex() - iter.getBeginIndex()];
165: embeddings = new byte[text.length];
166: embeddingOffset = 0;
167: length = text.length;
168: for (int i = 0; i < text.length; ++i)
169: {
170: text[i] = iter.current();
171:
172: val = iter.getAttribute(TextAttribute.BIDI_EMBEDDING);
173: if (val instanceof Integer)
174: {
175: int ival = ((Integer) val).intValue();
176: byte bval;
177: if (ival < -62 || ival > 62)
178: bval = 0;
179: else
180: bval = (byte) ival;
181: embeddings[i] = bval;
182: }
183: }
184:
185:
186: if (shaper != null)
187: shaper.shape(text, 0, length);
188:
189: runBidi();
190: }
191:
192:
208: public Bidi(char[] text, int offset, byte[] embeddings, int embedOffset,
209: int length, int flags)
210: {
211: if (flags != DIRECTION_DEFAULT_LEFT_TO_RIGHT
212: && flags != DIRECTION_DEFAULT_RIGHT_TO_LEFT
213: && flags != DIRECTION_LEFT_TO_RIGHT
214: && flags != DIRECTION_RIGHT_TO_LEFT)
215: throw new IllegalArgumentException("unrecognized 'flags' argument: "
216: + flags);
217: this.text = text;
218: this.textOffset = offset;
219: this.embeddings = embeddings;
220: this.embeddingOffset = embedOffset;
221: this.length = length;
222: this.flags = flags;
223:
224: runBidi();
225: }
226:
227:
233: public Bidi(String text, int flags)
234: {
235: if (flags != DIRECTION_DEFAULT_LEFT_TO_RIGHT
236: && flags != DIRECTION_DEFAULT_RIGHT_TO_LEFT
237: && flags != DIRECTION_LEFT_TO_RIGHT
238: && flags != DIRECTION_RIGHT_TO_LEFT)
239: throw new IllegalArgumentException("unrecognized 'flags' argument: "
240: + flags);
241:
242:
243:
244:
245: this.text = text.toCharArray();
246: this.textOffset = 0;
247: this.embeddings = null;
248: this.embeddingOffset = 0;
249: this.length = text.length();
250: this.flags = flags;
251:
252: runBidi();
253: }
254:
255:
259: private void computeTypes()
260: {
261: types = new byte[length];
262: for (int i = 0; i < length; ++i)
263: types[i] = Character.getDirectionality(text[textOffset + i]);
264: }
265:
266:
271: private int computeParagraphEmbeddingLevel()
272: {
273:
274: if (flags == DIRECTION_LEFT_TO_RIGHT
275: || flags == DIRECTION_RIGHT_TO_LEFT)
276: return flags;
277:
278:
279:
280:
281: for (int i = 0; i < length; ++i)
282: {
283: int dir = types[i];
284: if (dir == Character.DIRECTIONALITY_LEFT_TO_RIGHT)
285: return DIRECTION_LEFT_TO_RIGHT;
286: if (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT
287: || dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT)
288: return DIRECTION_RIGHT_TO_LEFT;
289: }
290: return (flags == DIRECTION_DEFAULT_LEFT_TO_RIGHT
291: ? DIRECTION_LEFT_TO_RIGHT
292: : DIRECTION_RIGHT_TO_LEFT);
293: }
294:
295:
300: private void computeExplicitLevels()
301: {
302: levels = new byte[length];
303: byte currentEmbedding = (byte) baseEmbedding;
304:
305:
306: byte directionalOverride = -1;
307:
308:
309:
310:
311: final int MAX_DEPTH = 62;
312: byte[] embeddingStack = new byte[MAX_DEPTH];
313: int sp = 0;
314:
315: for (int i = 0; i < length; ++i)
316: {
317:
318:
319: if (embeddings != null && embeddings[embeddingOffset + i] != 0)
320: {
321:
322:
323:
324: currentEmbedding = embeddings[embeddingOffset + i];
325: if (currentEmbedding < 0)
326: {
327: currentEmbedding = (byte) -currentEmbedding;
328: directionalOverride
329: = (((currentEmbedding % 2) == 0)
330: ? Character.DIRECTIONALITY_LEFT_TO_RIGHT
331: : Character.DIRECTIONALITY_RIGHT_TO_LEFT);
332: }
333: else
334: directionalOverride = -1;
335: continue;
336: }
337:
338: boolean isLtoR = false;
339: boolean isSpecial = true;
340: switch (types[i])
341: {
342: case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
343: case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
344: isLtoR = true;
345:
346: case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
347: case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
348: {
349: byte newEmbedding;
350: if (isLtoR)
351: {
352:
353: newEmbedding = (byte) ((currentEmbedding & ~1) + 2);
354: }
355: else
356: {
357:
358: newEmbedding = (byte) ((currentEmbedding + 1) | 1);
359: }
360:
361: if (newEmbedding < MAX_DEPTH)
362: {
363:
364:
365: if (directionalOverride != -1)
366: currentEmbedding |= Byte.MIN_VALUE;
367: embeddingStack[sp++] = currentEmbedding;
368: currentEmbedding = newEmbedding;
369: if (types[i] == Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE)
370: directionalOverride = Character.DIRECTIONALITY_LEFT_TO_RIGHT;
371: else if (types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE)
372: directionalOverride = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
373: else
374: directionalOverride = -1;
375: }
376: }
377: break;
378: case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT:
379: {
380:
381:
382: if (sp == 0)
383: {
384:
385: break;
386: }
387: byte newEmbedding = embeddingStack[--sp];
388: currentEmbedding = (byte) (newEmbedding & 0x7f);
389: if (newEmbedding < 0)
390: directionalOverride
391: = (((newEmbedding & 1) == 0)
392: ? Character.DIRECTIONALITY_LEFT_TO_RIGHT
393: : Character.DIRECTIONALITY_RIGHT_TO_LEFT);
394: else
395: directionalOverride = -1;
396: }
397: break;
398: default:
399: isSpecial = false;
400: break;
401: }
402: levels[i] = currentEmbedding;
403: if (isSpecial)
404: {
405:
406: if (formatterIndices == null)
407: formatterIndices = new ArrayList<Integer>();
408: formatterIndices.add(Integer.valueOf(i));
409: }
410: else if (directionalOverride != -1)
411: types[i] = directionalOverride;
412: }
413:
414:
415:
416:
417:
418:
419:
420: if (formatterIndices == null)
421: return;
422: int output = 0, input = 0;
423: final int size = formatterIndices.size();
424: for (int i = 0; i <= size; ++i)
425: {
426: int nextFmt;
427: if (i == size)
428: nextFmt = length;
429: else
430: nextFmt = formatterIndices.get(i).intValue();
431:
432: int len = nextFmt - input;
433: System.arraycopy(levels, input, levels, output, len);
434: System.arraycopy(types, input, types, output, len);
435: output += len;
436: input = nextFmt + 1;
437: }
438: length -= formatterIndices.size();
439: }
440:
441:
449: private void computeRuns()
450: {
451: int runCount = 0;
452: int currentEmbedding = baseEmbedding;
453: for (int i = 0; i < length; ++i)
454: {
455: if (levels[i] != currentEmbedding)
456: {
457: currentEmbedding = levels[i];
458: ++runCount;
459: }
460: }
461:
462:
463:
464:
465: if (runs == null || runs.length != runCount + 1)
466: runs = new int[runCount + 1];
467: int where = 0;
468: int lastRunStart = 0;
469: currentEmbedding = baseEmbedding;
470: for (int i = 0; i < length; ++i)
471: {
472: if (levels[i] != currentEmbedding)
473: {
474: runs[where++] = lastRunStart;
475: lastRunStart = i;
476: currentEmbedding = levels[i];
477: }
478: }
479: runs[where++] = lastRunStart;
480: }
481:
482:
486: private void resolveWeakTypes()
487: {
488: final int runCount = getRunCount();
489:
490: int previousLevel = baseEmbedding;
491: for (int run = 0; run < runCount; ++run)
492: {
493: int start = getRunStart(run);
494: int end = getRunLimit(run);
495: int level = getRunLevel(run);
496:
497:
498: byte sor = (((Math.max(previousLevel, level) % 2) == 0)
499: ? Character.DIRECTIONALITY_LEFT_TO_RIGHT
500: : Character.DIRECTIONALITY_RIGHT_TO_LEFT);
501: int nextLevel;
502: if (run == runCount - 1)
503: nextLevel = baseEmbedding;
504: else
505: nextLevel = getRunLevel(run + 1);
506: byte eor = (((Math.max(level, nextLevel) % 2) == 0)
507: ? Character.DIRECTIONALITY_LEFT_TO_RIGHT
508: : Character.DIRECTIONALITY_RIGHT_TO_LEFT);
509:
510: byte prevType = sor;
511: byte prevStrongType = sor;
512: for (int i = start; i < end; ++i)
513: {
514: final byte nextType = (i == end - 1) ? eor : types[i + 1];
515:
516:
517: if (types[i] == Character.DIRECTIONALITY_NONSPACING_MARK)
518: types[i] = prevType;
519: else
520: prevType = types[i];
521:
522:
523: if (types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
524: {
525: if (prevStrongType == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
526: types[i] = Character.DIRECTIONALITY_ARABIC_NUMBER;
527: }
528: else if (types[i] == Character.DIRECTIONALITY_LEFT_TO_RIGHT
529: || types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT
530: || types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
531: prevStrongType = types[i];
532:
533:
534: if (types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
535: types[i] = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
536:
537:
538: if (prevType == Character.DIRECTIONALITY_EUROPEAN_NUMBER
539: && nextType == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
540: {
541: if (types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR
542: || types[i] == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR)
543: types[i] = nextType;
544: }
545: else if (prevType == Character.DIRECTIONALITY_ARABIC_NUMBER
546: && nextType == Character.DIRECTIONALITY_ARABIC_NUMBER
547: && types[i] == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR)
548: types[i] = nextType;
549:
550:
551:
552:
553: if (types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR
554: || types[i] == Character.DIRECTIONALITY_BOUNDARY_NEUTRAL)
555: {
556: if (prevType == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
557: types[i] = prevType;
558: else
559: {
560:
561:
562: int j = i + 1;
563: while (j < end
564: && (types[j] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR
565: || types[j] == Character.DIRECTIONALITY_BOUNDARY_NEUTRAL))
566: ++j;
567: if (j < end
568: && types[j] == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
569: {
570:
571: for (int k = i; k < j; ++k)
572: types[k] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
573: }
574: }
575: }
576:
577:
578:
579: if (types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR
580: || types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR
581: || types[i] == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR
582: || types[i] == Character.DIRECTIONALITY_BOUNDARY_NEUTRAL)
583: types[i] = Character.DIRECTIONALITY_OTHER_NEUTRALS;
584:
585:
586: if (prevStrongType == Character.DIRECTIONALITY_LEFT_TO_RIGHT
587: && types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
588: types[i] = prevStrongType;
589: }
590:
591: previousLevel = level;
592: }
593: }
594:
595:
599: private void resolveNeutralTypes()
600: {
601:
602: final int runCount = getRunCount();
603:
604: int previousLevel = baseEmbedding;
605: for (int run = 0; run < runCount; ++run)
606: {
607: int start = getRunStart(run);
608: int end = getRunLimit(run);
609: int level = getRunLevel(run);
610:
611: byte embeddingDirection
612: = (((level % 2) == 0) ? Character.DIRECTIONALITY_LEFT_TO_RIGHT
613: : Character.DIRECTIONALITY_RIGHT_TO_LEFT);
614:
615: byte sor = (((Math.max(previousLevel, level) % 2) == 0)
616: ? Character.DIRECTIONALITY_LEFT_TO_RIGHT
617: : Character.DIRECTIONALITY_RIGHT_TO_LEFT);
618: int nextLevel;
619: if (run == runCount - 1)
620: nextLevel = baseEmbedding;
621: else
622: nextLevel = getRunLevel(run + 1);
623: byte eor = (((Math.max(level, nextLevel) % 2) == 0)
624: ? Character.DIRECTIONALITY_LEFT_TO_RIGHT
625: : Character.DIRECTIONALITY_RIGHT_TO_LEFT);
626:
627: byte prevStrong = sor;
628: int neutralStart = -1;
629: for (int i = start; i <= end; ++i)
630: {
631: byte newStrong = -1;
632: byte thisType = i == end ? eor : types[i];
633: switch (thisType)
634: {
635: case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
636: newStrong = Character.DIRECTIONALITY_LEFT_TO_RIGHT;
637: break;
638: case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
639: case Character.DIRECTIONALITY_ARABIC_NUMBER:
640: case Character.DIRECTIONALITY_EUROPEAN_NUMBER:
641: newStrong = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
642: break;
643: case Character.DIRECTIONALITY_BOUNDARY_NEUTRAL:
644: case Character.DIRECTIONALITY_OTHER_NEUTRALS:
645: case Character.DIRECTIONALITY_SEGMENT_SEPARATOR:
646: case Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR:
647: case Character.DIRECTIONALITY_WHITESPACE:
648: if (neutralStart == -1)
649: neutralStart = i;
650: break;
651: }
652:
653: if (newStrong != -1)
654: {
655: if (neutralStart != -1)
656: {
657: byte override = (prevStrong == newStrong
658: ? prevStrong
659: : embeddingDirection);
660: for (int j = neutralStart; j < i; ++j)
661: types[j] = override;
662: }
663: prevStrong = newStrong;
664: neutralStart = -1;
665: }
666: }
667:
668: previousLevel = level;
669: }
670: }
671:
672:
676: private void resolveImplicitLevels()
677: {
678:
679: for (int i = 0; i < length; ++i)
680: {
681: if ((levels[i] & 1) == 0)
682: {
683: if (types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT)
684: ++levels[i];
685: else if (types[i] == Character.DIRECTIONALITY_ARABIC_NUMBER
686: || types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
687: levels[i] += 2;
688: }
689: else
690: {
691: if (types[i] == Character.DIRECTIONALITY_LEFT_TO_RIGHT
692: || types[i] == Character.DIRECTIONALITY_ARABIC_NUMBER
693: || types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
694: ++levels[i];
695: }
696:
697:
698: resultFlags |= 1 << (levels[i] & 1);
699: }
700:
701: resultFlags |= 1 << baseEmbedding;
702: }
703:
704:
710: private void reinsertFormattingCodes()
711: {
712: if (formatterIndices == null)
713: return;
714: int input = length;
715: int output = levels.length;
716:
717: for (int index = formatterIndices.size() - 1; index >= 0; --index)
718: {
719: int nextFmt = formatterIndices.get(index).intValue();
720:
721:
722:
723:
724:
725: int len = output - nextFmt - 1;
726: output = nextFmt;
727: input -= len;
728:
729:
730: if (nextFmt + 1 < levels.length)
731: System.arraycopy(levels, input, levels, nextFmt + 1, len);
732:
733:
734: int rightLevel;
735: if (output == levels.length - 1)
736: rightLevel = baseEmbedding;
737: else
738: rightLevel = levels[output + 1];
739: int leftLevel;
740: if (input == 0)
741: leftLevel = baseEmbedding;
742: else
743: leftLevel = levels[input];
744: levels[output] = (byte) Math.max(leftLevel, rightLevel);
745: }
746: length = levels.length;
747: }
748:
749:
754: private void runBidi()
755: {
756: computeTypes();
757: baseEmbedding = computeParagraphEmbeddingLevel();
758: computeExplicitLevels();
759: computeRuns();
760: resolveWeakTypes();
761: resolveNeutralTypes();
762: resolveImplicitLevels();
763:
764: types = null;
765: reinsertFormattingCodes();
766:
767:
768: computeRuns();
769: }
770:
771:
775: public boolean baseIsLeftToRight()
776: {
777: return baseEmbedding == DIRECTION_LEFT_TO_RIGHT;
778: }
779:
780:
787: public Bidi createLineBidi(int start, int end)
788: {
789:
790:
791: int level = getLevelAt(start);
792: int flag = (((level % 2) == 0)
793: ? DIRECTION_LEFT_TO_RIGHT
794: : DIRECTION_RIGHT_TO_LEFT);
795: return new Bidi(text, textOffset + start,
796: embeddings, embeddingOffset + start,
797: end - start, flag);
798: }
799:
800:
803: public int getBaseLevel()
804: {
805: return baseEmbedding;
806: }
807:
808:
811: public int getLength()
812: {
813: return length;
814: }
815:
816:
824: public int getLevelAt(int offset)
825: {
826: if (offset < 0 || offset >= length)
827: return getBaseLevel();
828: return levels[offset];
829: }
830:
831:
835: public int getRunCount()
836: {
837: return runs.length;
838: }
839:
840:
845: public int getRunLevel(int which)
846: {
847: return levels[runs[which]];
848: }
849:
850:
857: public int getRunLimit(int which)
858: {
859: if (which == runs.length - 1)
860: return length;
861: return runs[which + 1];
862: }
863:
864:
869: public int getRunStart(int which)
870: {
871: return runs[which];
872: }
873:
874:
878: public boolean isLeftToRight()
879: {
880: return resultFlags == LTOR;
881: }
882:
883:
888: public boolean isMixed()
889: {
890: return resultFlags == (LTOR | RTOL);
891: }
892:
893:
897: public boolean isRightToLeft()
898: {
899: return resultFlags == RTOL;
900: }
901:
902:
906: public String toString()
907: {
908: return "Bidi Bidi Bidi I like you, Buck!";
909: }
910:
911:
923: public static void reorderVisually(byte[] levels, int levelOffset,
924: Object[] objs, int objOffset, int count)
925: {
926:
927:
928: byte[] levelCopy = new byte[count];
929:
930:
931: int max = 0;
932: int lowestOdd = 63;
933: for (int i = 0; i < count; ++i)
934: {
935: levelCopy[i] = levels[levelOffset + i];
936: max = Math.max(levelCopy[i], max);
937: if (levelCopy[i] % 2 != 0)
938: lowestOdd = Math.min(lowestOdd, levelCopy[i]);
939: }
940:
941:
942: for (int depth = max; depth >= lowestOdd; --depth)
943: {
944: int start = 0;
945: while (start < count)
946: {
947:
948: while (start < count && levelCopy[start] < depth)
949: ++start;
950: if (start == count)
951: break;
952:
953: int end = start + 1;
954: while (end < count && levelCopy[end] >= depth)
955: ++end;
956:
957:
958: for (int i = 0; i < (end - start) / 2; ++i)
959: {
960: byte tmpb = levelCopy[end - i - 1];
961: levelCopy[end - i - 1] = levelCopy[start + i];
962: levelCopy[start + i] = tmpb;
963: Object tmpo = objs[objOffset + end - i - 1];
964: objs[objOffset + end - i - 1] = objs[objOffset + start + i];
965: objs[objOffset + start + i] = tmpo;
966: }
967:
968:
969: start = end + 1;
970: }
971: }
972: }
973:
974:
982: public static boolean requiresBidi(char[] text, int start, int end)
983: {
984: for (int i = start; i < end; i++)
985: {
986: byte dir = Character.getDirectionality(text[i]);
987: if (dir != Character.DIRECTIONALITY_LEFT_TO_RIGHT
988: && dir != Character.DIRECTIONALITY_EUROPEAN_NUMBER
989: && dir != Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR
990: && dir != Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR
991: && dir != Character.DIRECTIONALITY_ARABIC_NUMBER
992: && dir != Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR
993: && dir != Character.DIRECTIONALITY_SEGMENT_SEPARATOR
994: && dir != Character.DIRECTIONALITY_WHITESPACE
995: && dir != Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR)
996: return true;
997: }
998:
999: return false;
1000: }
1001: }