1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46:
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52:
53:
65: public class DefaultStyledDocument extends AbstractDocument
66: implements StyledDocument
67: {
68:
73: public static class AttributeUndoableEdit
74: extends AbstractUndoableEdit
75: {
76:
79: protected AttributeSet copy;
80:
81:
84: protected AttributeSet newAttributes;
85:
86:
90: protected boolean isReplacing;
91:
92:
95: protected Element element;
96:
97:
105: public AttributeUndoableEdit(Element el, AttributeSet newAtts,
106: boolean replacing)
107: {
108: element = el;
109: newAttributes = newAtts;
110: isReplacing = replacing;
111: copy = el.getAttributes().copyAttributes();
112: }
113:
114:
118: public void undo()
119: {
120: super.undo();
121: AttributeSet atts = element.getAttributes();
122: if (atts instanceof MutableAttributeSet)
123: {
124: MutableAttributeSet mutable = (MutableAttributeSet) atts;
125: mutable.removeAttributes(atts);
126: mutable.addAttributes(copy);
127: }
128: }
129:
130:
135: public void redo()
136: {
137: super.undo();
138: AttributeSet atts = element.getAttributes();
139: if (atts instanceof MutableAttributeSet)
140: {
141: MutableAttributeSet mutable = (MutableAttributeSet) atts;
142: if (isReplacing)
143: mutable.removeAttributes(atts);
144: mutable.addAttributes(newAttributes);
145: }
146: }
147: }
148:
149:
154: public static class ElementSpec
155: {
156:
160: public static final short StartTagType = 1;
161:
162:
166: public static final short EndTagType = 2;
167:
168:
172: public static final short ContentType = 3;
173:
174:
179: public static final short JoinPreviousDirection = 4;
180:
181:
186: public static final short JoinNextDirection = 5;
187:
188:
193: public static final short OriginateDirection = 6;
194:
195:
200: public static final short JoinFractureDirection = 7;
201:
202:
205: short type;
206:
207:
210: short direction;
211:
212:
215: int offset;
216:
217:
220: int length;
221:
222:
225: char[] content;
226:
227:
230: AttributeSet attributes;
231:
232:
239: public ElementSpec(AttributeSet a, short type)
240: {
241: this(a, type, 0);
242: }
243:
244:
253: public ElementSpec(AttributeSet a, short type, int len)
254: {
255: this(a, type, null, 0, len);
256: }
257:
258:
267: public ElementSpec(AttributeSet a, short type, char[] txt, int offs,
268: int len)
269: {
270: attributes = a;
271: this.type = type;
272: offset = offs;
273: length = len;
274: content = txt;
275: direction = OriginateDirection;
276: }
277:
278:
283: public void setType(short type)
284: {
285: this.type = type;
286: }
287:
288:
293: public short getType()
294: {
295: return type;
296: }
297:
298:
303: public void setDirection(short dir)
304: {
305: direction = dir;
306: }
307:
308:
313: public short getDirection()
314: {
315: return direction;
316: }
317:
318:
323: public AttributeSet getAttributes()
324: {
325: return attributes;
326: }
327:
328:
333: public char[] getArray()
334: {
335: return content;
336: }
337:
338:
343: public int getOffset()
344: {
345: return offset;
346: }
347:
348:
353: public int getLength()
354: {
355: return length;
356: }
357:
358:
365: public String toString()
366: {
367: StringBuilder b = new StringBuilder();
368: b.append('<');
369: switch (type)
370: {
371: case StartTagType:
372: b.append("StartTag");
373: break;
374: case EndTagType:
375: b.append("EndTag");
376: break;
377: case ContentType:
378: b.append("Content");
379: break;
380: default:
381: b.append("??");
382: break;
383: }
384:
385: b.append(':');
386:
387: switch (direction)
388: {
389: case JoinPreviousDirection:
390: b.append("JoinPrevious");
391: break;
392: case JoinNextDirection:
393: b.append("JoinNext");
394: break;
395: case OriginateDirection:
396: b.append("Originate");
397: break;
398: case JoinFractureDirection:
399: b.append("Fracture");
400: break;
401: default:
402: b.append("??");
403: break;
404: }
405:
406: b.append(':');
407: b.append(length);
408:
409: return b.toString();
410: }
411: }
412:
413:
417: public class ElementBuffer implements Serializable
418: {
419:
420: private static final long serialVersionUID = 1688745877691146623L;
421:
422:
423: private Element root;
424:
425:
426: private int offset;
427:
428:
429: private int length;
430:
431:
437: Element[] fracture;
438:
439:
442: DefaultDocumentEvent documentEvent;
443:
444:
450: public ElementBuffer(Element root)
451: {
452: this.root = root;
453: }
454:
455:
460: public Element getRootElement()
461: {
462: return root;
463: }
464:
465:
479: public void change(int offset, int length, DefaultDocumentEvent ev)
480: {
481: this.offset = offset;
482: this.length = length;
483: documentEvent = ev;
484: changeUpdate();
485: }
486:
487:
492: protected void changeUpdate()
493: {
494:
495: Element el = getCharacterElement(offset);
496: split(el, offset);
497:
498: int endOffset = offset + length;
499: el = getCharacterElement(endOffset);
500: split(el, endOffset);
501: }
502:
503:
509: void split(Element el, int offset)
510: {
511: if (el instanceof AbstractElement)
512: {
513: AbstractElement ael = (AbstractElement) el;
514: int startOffset = ael.getStartOffset();
515: int endOffset = ael.getEndOffset();
516: int len = endOffset - startOffset;
517: if (startOffset != offset && endOffset != offset)
518: {
519: Element paragraph = ael.getParentElement();
520: if (paragraph instanceof BranchElement)
521: {
522: BranchElement par = (BranchElement) paragraph;
523: Element child1 = createLeafElement(par, ael, startOffset,
524: offset);
525: Element child2 = createLeafElement(par, ael, offset,
526: endOffset);
527: int index = par.getElementIndex(startOffset);
528: Element[] add = new Element[]{ child1, child2 };
529: par.replace(index, 1, add);
530: documentEvent.addEdit(new ElementEdit(par, index,
531: new Element[]{ el },
532: add));
533: }
534: else
535: throw new AssertionError("paragraph elements are expected to "
536: + "be instances of "
537: + "javax.swing.text.AbstractDocument.BranchElement");
538: }
539: }
540: else
541: throw new AssertionError("content elements are expected to be "
542: + "instances of "
543: + "javax.swing.text.AbstractDocument.AbstractElement");
544: }
545:
546:
560: public void insert(int offset, int length, ElementSpec[] data,
561: DefaultDocumentEvent ev)
562: {
563: this.offset = offset;
564: this.length = length;
565: documentEvent = ev;
566: insertUpdate(data);
567: }
568:
569:
577: protected void insertUpdate(ElementSpec[] data)
578: {
579: for (int i = 0; i < data.length; i++)
580: {
581: switch (data[i].getType())
582: {
583: case ElementSpec.StartTagType:
584: insertStartTag(data[i]);
585: break;
586: case ElementSpec.EndTagType:
587: insertEndTag(data[i]);
588: break;
589: default:
590: insertContentTag(data[i]);
591: break;
592: }
593: }
594: }
595:
596:
601: void insertStartTag(ElementSpec tag)
602: {
603: BranchElement root = (BranchElement) getDefaultRootElement();
604: int index = root.getElementIndex(offset);
605: if (index == -1)
606: index = 0;
607:
608: BranchElement newParagraph =
609: (BranchElement) createBranchElement(root, tag.getAttributes());
610: newParagraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE));
611:
612:
613: Element[] added = new Element[]{newParagraph};
614: root.replace(index + 1, 0, added);
615: ElementEdit edit = new ElementEdit(root, index + 1, new Element[0],
616: added);
617: documentEvent.addEdit(edit);
618:
619:
620: if (tag.getDirection() == ElementSpec.JoinFractureDirection)
621: {
622: Element[] newFracture = new Element[fracture.length];
623: for (int i = 0; i < fracture.length; i++)
624: {
625: Element oldLeaf = fracture[i];
626: Element newLeaf = createLeafElement(newParagraph,
627: oldLeaf.getAttributes(),
628: oldLeaf.getStartOffset(),
629: oldLeaf.getEndOffset());
630: newFracture[i] = newLeaf;
631: }
632: newParagraph.replace(0, 0, newFracture);
633: edit = new ElementEdit(newParagraph, 0, new Element[0],
634: fracture);
635: documentEvent.addEdit(edit);
636: fracture = new Element[0];
637: }
638: }
639:
640:
646: void insertEndTag(ElementSpec tag)
647: {
648: BranchElement root = (BranchElement) getDefaultRootElement();
649: int parIndex = root.getElementIndex(offset);
650: BranchElement paragraph = (BranchElement) root.getElement(parIndex);
651:
652: int index = paragraph.getElementIndex(offset);
653: LeafElement content = (LeafElement) paragraph.getElement(index);
654:
655: split(content, offset);
656: index = paragraph.getElementIndex(offset);
657:
658: int count = paragraph.getElementCount();
659:
660: fracture = new Element[count - index];
661: for (int i = index; i < count; ++i)
662: fracture[i - index] = paragraph.getElement(i);
663:
664:
665: paragraph.replace(index, count - index, new Element[0]);
666:
667:
668: ElementEdit edit = new ElementEdit(paragraph, index, fracture,
669: new Element[0]);
670: documentEvent.addEdit(edit);
671: }
672:
673:
678: void insertContentTag(ElementSpec tag)
679: {
680: int len = tag.getLength();
681: int dir = tag.getDirection();
682: if (dir == ElementSpec.JoinPreviousDirection)
683: {
684: Element prev = getCharacterElement(offset);
685: BranchElement prevParent = (BranchElement) prev.getParentElement();
686: Element join = createLeafElement(prevParent, tag.getAttributes(),
687: prev.getStartOffset(),
688: Math.max(prev.getEndOffset(),
689: offset + len));
690: int ind = prevParent.getElementIndex(offset);
691: if (ind == -1)
692: ind = 0;
693: Element[] add = new Element[]{join};
694: prevParent.replace(ind, 1, add);
695:
696:
697: ElementEdit edit = new ElementEdit(prevParent, ind,
698: new Element[]{prev}, add);
699: documentEvent.addEdit(edit);
700: }
701: else if (dir == ElementSpec.JoinNextDirection)
702: {
703: Element next = getCharacterElement(offset + len);
704: BranchElement nextParent = (BranchElement) next.getParentElement();
705: Element join = createLeafElement(nextParent, tag.getAttributes(),
706: offset,
707: next.getEndOffset());
708: int ind = nextParent.getElementIndex(offset + len);
709: if (ind == -1)
710: ind = 0;
711: Element[] add = new Element[]{join};
712: nextParent.replace(ind, 1, add);
713:
714:
715: ElementEdit edit = new ElementEdit(nextParent, ind,
716: new Element[]{next}, add);
717: documentEvent.addEdit(edit);
718: }
719: else
720: {
721: BranchElement par = (BranchElement) getParagraphElement(offset);
722:
723: int ind = par.getElementIndex(offset);
724:
725:
726:
727: Element prev = par.getElement(ind);
728: if (prev != null && prev.getStartOffset() < offset)
729: {
730: Element cutPrev = createLeafElement(par, prev.getAttributes(),
731: prev.getStartOffset(),
732: offset);
733: Element[] remove = new Element[]{prev};
734: Element[] add = new Element[]{cutPrev};
735: if (prev.getEndOffset() > offset + len)
736: {
737: Element rem = createLeafElement(par, prev.getAttributes(),
738: offset + len,
739: prev.getEndOffset());
740: add = new Element[]{cutPrev, rem};
741: }
742:
743: par.replace(ind, 1, add);
744: documentEvent.addEdit(new ElementEdit(par, ind, remove, add));
745: ind++;
746: }
747:
748:
749:
750: Element next = par.getElement(ind);
751: if (next != null && next.getStartOffset() < offset + len)
752: {
753: Element cutNext = createLeafElement(par, next.getAttributes(),
754: offset + len,
755: next.getEndOffset());
756: Element[] remove = new Element[]{next};
757: Element[] add = new Element[]{cutNext};
758: par.replace(ind, 1, add);
759: documentEvent.addEdit(new ElementEdit(par, ind, remove,
760: add));
761: }
762:
763:
764: Element newEl = createLeafElement(par, tag.getAttributes(),
765: offset, offset + len);
766: Element[] added = new Element[]{newEl};
767: par.replace(ind, 0, added);
768:
769: ElementEdit edit = new ElementEdit(par, ind, new Element[0],
770: added);
771: documentEvent.addEdit(edit);
772: }
773: offset += len;
774: }
775:
776:
783: public Element clone (Element parent, Element clonee)
784: {
785:
786: if (clonee.isLeaf())
787: return createLeafElement(parent, clonee.getAttributes(),
788: clonee.getStartOffset(), clonee.getEndOffset());
789:
790:
791:
792: BranchElement result = (BranchElement) createBranchElement(parent, clonee.getAttributes());
793:
794:
795: Element[] children = new Element[clonee.getElementCount()];
796: for (int i = 0; i < children.length; i++)
797: children[i] = clone(result, clonee.getElement(i));
798:
799:
800: result.replace(0, 0, children);
801: return result;
802: }
803: }
804:
805:
809: protected class SectionElement extends BranchElement
810: {
811:
814: public SectionElement()
815: {
816: super(null, null);
817: }
818:
819:
825: public String getName()
826: {
827: return "section";
828: }
829: }
830:
831:
837: private class StyleChangeListener
838: implements ChangeListener
839: {
840:
841:
847: public void stateChanged(ChangeEvent event)
848: {
849: Style style = (Style) event.getSource();
850: styleChanged(style);
851: }
852: }
853:
854:
855: private static final long serialVersionUID = 940485415728614849L;
856:
857:
860: public static final int BUFFER_SIZE_DEFAULT = 4096;
861:
862:
866: protected DefaultStyledDocument.ElementBuffer buffer;
867:
868:
871: private StyleChangeListener styleChangeListener;
872:
873:
876: public DefaultStyledDocument()
877: {
878: this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleContext());
879: }
880:
881:
887: public DefaultStyledDocument(StyleContext context)
888: {
889: this(new GapContent(BUFFER_SIZE_DEFAULT), context);
890: }
891:
892:
899: public DefaultStyledDocument(AbstractDocument.Content content,
900: StyleContext context)
901: {
902: super(content, context);
903: buffer = new ElementBuffer(createDefaultRoot());
904: setLogicalStyle(0, context.getStyle(StyleContext.DEFAULT_STYLE));
905: }
906:
907:
923: public Style addStyle(String nm, Style parent)
924: {
925: StyleContext context = (StyleContext) getAttributeContext();
926: Style newStyle = context.addStyle(nm, parent);
927:
928:
929: if (styleChangeListener == null)
930: styleChangeListener = new StyleChangeListener();
931: newStyle.addChangeListener(styleChangeListener);
932:
933: return newStyle;
934: }
935:
936:
941: protected AbstractDocument.AbstractElement createDefaultRoot()
942: {
943: Element[] tmp;
944:
945:
946: SectionElement section = new SectionElement();
947:
948: BranchElement paragraph =
949: (BranchElement) createBranchElement(section, null);
950: paragraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE));
951: tmp = new Element[1];
952: tmp[0] = paragraph;
953: section.replace(0, 0, tmp);
954:
955: LeafElement leaf = new LeafElement(paragraph, null, 0, 1);
956: tmp = new Element[1];
957: tmp[0] = leaf;
958: paragraph.replace(0, 0, tmp);
959:
960: return section;
961: }
962:
963:
973: public Element getCharacterElement(int position)
974: {
975: Element element = getDefaultRootElement();
976:
977: while (!element.isLeaf())
978: {
979: int index = element.getElementIndex(position);
980: element = element.getElement(index);
981: }
982:
983: return element;
984: }
985:
986:
993: public Color getBackground(AttributeSet attributes)
994: {
995: StyleContext context = (StyleContext) getAttributeContext();
996: return context.getBackground(attributes);
997: }
998:
999:
1004: public Element getDefaultRootElement()
1005: {
1006: return buffer.getRootElement();
1007: }
1008:
1009:
1016: public Font getFont(AttributeSet attributes)
1017: {
1018: StyleContext context = (StyleContext) getAttributeContext();
1019: return context.getFont(attributes);
1020: }
1021:
1022:
1029: public Color getForeground(AttributeSet attributes)
1030: {
1031: StyleContext context = (StyleContext) getAttributeContext();
1032: return context.getForeground(attributes);
1033: }
1034:
1035:
1042: public Style getLogicalStyle(int position)
1043: {
1044: Element paragraph = getParagraphElement(position);
1045: AttributeSet attributes = paragraph.getAttributes();
1046: return (Style) attributes.getResolveParent();
1047: }
1048:
1049:
1060: public Element getParagraphElement(int position)
1061: {
1062: BranchElement root = (BranchElement) getDefaultRootElement();
1063: int start = root.getStartOffset();
1064: int end = root.getEndOffset();
1065: if (position >= end)
1066: position = end - 1;
1067: else if (position < start)
1068: position = start;
1069:
1070: Element par = root.positionToElement(position);
1071:
1072: assert par != null : "The paragraph element must not be null";
1073: return par;
1074: }
1075:
1076:
1084: public Style getStyle(String nm)
1085: {
1086: StyleContext context = (StyleContext) getAttributeContext();
1087: return context.getStyle(nm);
1088: }
1089:
1090:
1095: public void removeStyle(String nm)
1096: {
1097: StyleContext context = (StyleContext) getAttributeContext();
1098: context.removeStyle(nm);
1099: }
1100:
1101:
1111: public void setCharacterAttributes(int offset, int length,
1112: AttributeSet attributes,
1113: boolean replace)
1114: {
1115: DefaultDocumentEvent ev =
1116: new DefaultDocumentEvent(offset, length,
1117: DocumentEvent.EventType.CHANGE);
1118:
1119:
1120:
1121: buffer.change(offset, length, ev);
1122:
1123: Element root = getDefaultRootElement();
1124:
1125: int paragraphCount = root.getElementCount();
1126: for (int pindex = 0; pindex < paragraphCount; pindex++)
1127: {
1128: Element paragraph = root.getElement(pindex);
1129:
1130: if ((paragraph.getStartOffset() > offset + length)
1131: || (paragraph.getEndOffset() < offset))
1132: continue;
1133:
1134:
1135: int contentCount = paragraph.getElementCount();
1136: for (int cindex = 0; cindex < contentCount; cindex++)
1137: {
1138: Element content = paragraph.getElement(cindex);
1139:
1140: if ((content.getStartOffset() > offset + length)
1141: || (content.getEndOffset() < offset))
1142: continue;
1143:
1144: if (content instanceof AbstractElement)
1145: {
1146: AbstractElement el = (AbstractElement) content;
1147: if (replace)
1148: el.removeAttributes(el);
1149: el.addAttributes(attributes);
1150: }
1151: else
1152: throw new AssertionError("content elements are expected to be"
1153: + "instances of "
1154: + "javax.swing.text.AbstractDocument.AbstractElement");
1155: }
1156: }
1157:
1158: fireChangedUpdate(ev);
1159: }
1160:
1161:
1167: public void setLogicalStyle(int position, Style style)
1168: {
1169: Element el = getParagraphElement(position);
1170: if (el instanceof AbstractElement)
1171: {
1172: AbstractElement ael = (AbstractElement) el;
1173: ael.setResolveParent(style);
1174: }
1175: else
1176: throw new AssertionError("paragraph elements are expected to be"
1177: + "instances of javax.swing.text.AbstractDocument.AbstractElement");
1178: }
1179:
1180:
1189: public void setParagraphAttributes(int offset, int length,
1190: AttributeSet attributes,
1191: boolean replace)
1192: {
1193: int index = offset;
1194: while (index < offset + length)
1195: {
1196: AbstractElement par = (AbstractElement) getParagraphElement(index);
1197: AttributeContext ctx = getAttributeContext();
1198: if (replace)
1199: par.removeAttributes(par);
1200: par.addAttributes(attributes);
1201: index = par.getElementCount();
1202: }
1203: }
1204:
1205:
1212: protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr)
1213: {
1214: super.insertUpdate(ev, attr);
1215: int offset = ev.getOffset();
1216: int length = ev.getLength();
1217: int endOffset = offset + length;
1218: Segment txt = new Segment();
1219: try
1220: {
1221: getText(offset, length, txt);
1222: }
1223: catch (BadLocationException ex)
1224: {
1225: AssertionError ae = new AssertionError("Unexpected bad location");
1226: ae.initCause(ex);
1227: throw ae;
1228: }
1229:
1230: int len = 0;
1231: Vector specs = new Vector();
1232:
1233: Element prev = getCharacterElement(offset);
1234: Element next = getCharacterElement(endOffset);
1235:
1236: for (int i = offset; i < endOffset; ++i)
1237: {
1238: len++;
1239: if (txt.array[i] == '\n')
1240: {
1241: ElementSpec spec = new ElementSpec(attr, ElementSpec.ContentType,
1242: len);
1243:
1244:
1245:
1246: if (i == endOffset - 1)
1247: {
1248: if (next.getAttributes().isEqual(attr))
1249: spec.setDirection(ElementSpec.JoinNextDirection);
1250: }
1251:
1252:
1253: else if (specs.size() == 0)
1254: {
1255: if (prev.getAttributes().isEqual(attr))
1256: spec.setDirection(ElementSpec.JoinPreviousDirection);
1257: }
1258:
1259: specs.add(spec);
1260:
1261:
1262: ElementSpec endTag = new ElementSpec(null, ElementSpec.EndTagType);
1263: specs.add(endTag);
1264: ElementSpec startTag = new ElementSpec(null,
1265: ElementSpec.StartTagType);
1266: startTag.setDirection(ElementSpec.JoinFractureDirection);
1267: specs.add(startTag);
1268:
1269: len = 0;
1270: offset += len;
1271: }
1272: }
1273:
1274:
1275: if (len > 0)
1276: {
1277: ElementSpec spec = new ElementSpec(attr, ElementSpec.ContentType, len);
1278:
1279:
1280: if (specs.size() == 0)
1281: {
1282: if (prev.getAttributes().isEqual(attr))
1283: spec.setDirection(ElementSpec.JoinPreviousDirection);
1284: }
1285:
1286: else if (next.getAttributes().isEqual(attr))
1287: spec.setDirection(ElementSpec.JoinNextDirection);
1288:
1289: specs.add(spec);
1290: }
1291:
1292: ElementSpec[] elSpecs =
1293: (ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]);
1294:
1295: buffer.insert(offset, length, elSpecs, ev);
1296: }
1297:
1298:
1303: public Enumeration getStyleNames()
1304: {
1305: StyleContext context = (StyleContext) getAttributeContext();
1306: return context.getStyleNames();
1307: }
1308:
1309:
1314: protected void styleChanged(Style style)
1315: {
1316:
1317: }
1318:
1319:
1325: protected void insert(int offset, ElementSpec[] data)
1326: throws BadLocationException
1327: {
1328: writeLock();
1329:
1330: int index = offset;
1331: for (int i = 0; i < data.length; i++)
1332: {
1333: ElementSpec spec = data[i];
1334: if (spec.getArray() != null && spec.getLength() > 0)
1335: {
1336: String insertString = new String(spec.getArray(), spec.getOffset(),
1337: spec.getLength());
1338: content.insertString(index, insertString);
1339: }
1340: index += spec.getLength();
1341: }
1342:
1343: DefaultDocumentEvent ev = new DefaultDocumentEvent(offset, index - offset,
1344: DocumentEvent.EventType.INSERT);
1345: for (int i = 0; i < data.length; i++)
1346: {
1347: ElementSpec spec = data[i];
1348: AttributeSet atts = spec.getAttributes();
1349: if (atts != null)
1350: insertUpdate(ev, atts);
1351: }
1352:
1353:
1354:
1355: buffer.insert(offset, index - offset, data, ev);
1356: fireInsertUpdate(ev);
1357: writeUnlock();
1358: }
1359:
1360:
1367: protected void create(ElementSpec[] data)
1368: {
1369: try
1370: {
1371:
1372: content.remove(0, content.length());
1373:
1374: buffer = new ElementBuffer(createDefaultRoot());
1375:
1376: insert(0, data);
1377: }
1378: catch (BadLocationException ex)
1379: {
1380: AssertionError err = new AssertionError("Unexpected bad location");
1381: err.initCause(ex);
1382: throw err;
1383: }
1384: }
1385: }