001package squidpony;
002
003import squidpony.squidmath.*;
004
005import java.util.*;
006
007/**
008 * Ways to produce concrete implementations of StringConvert for various data structures.
009 * Keeping the StringConvert producers separate from the data structures allows us to convert
010 * JDK types as well as to keep the parts that need ObText, and thus RegExodus, separate from
011 * the more general-use data structures.
012 * Created by Tommy Ettinger on 3/9/2017.
013 */
014@SuppressWarnings("unchecked")
015public class Converters {
016
017    public static void appendQuoted(StringBuilder sb, String text)
018    {
019        ObText.appendQuoted(sb, text);
020    }
021
022    public static ObText.ContentMatcher makeMatcher(CharSequence text)
023    {
024        return ObText.makeMatcherNoComments(text);
025    }
026    
027    public static <K> StringConvert<OrderedSet<K>> convertOrderedSet(final StringConvert<K> convert) {
028        CharSequence[] types = StringConvert.asArray("OrderedSet", convert.name);
029        StringConvert found = StringConvert.lookup(types);
030        if (found != null)
031            return found; // in this case we've already created a StringConvert for this type combination
032
033        return new StringConvert<OrderedSet<K>>(types) {
034            @Override
035            public String stringify(OrderedSet<K> item) {
036                StringBuilder sb = new StringBuilder(100);
037                K k;
038                for (int i = 0; i < item.size(); ) {
039                    k = item.getAt(i);
040                    if (item == k)
041                        return "";
042                    ObText.appendQuoted(sb, convert.stringify(k));
043                    if (++i < item.size())
044                        sb.append(' ');
045                }
046                return sb.toString();
047            }
048
049            @Override
050            public OrderedSet<K> restore(String text) {
051                ObText.ContentMatcher m = makeMatcher(text);
052                OrderedSet<K> d;
053                if(convert.isArray)
054                    d = new OrderedSet<>(CrossHash.generalHasher);
055                else
056                    d = new OrderedSet<>();
057                while (m.find()) {
058                    if (m.hasMatch()) {
059                        d.add(convert.restore(m.getMatch()));
060                    }
061                }
062                return d;
063            }
064        };
065    }
066
067    public static <K> StringConvert<OrderedSet<K>> convertOrderedSet(final CharSequence type) {
068        return convertOrderedSet((StringConvert<K>) StringConvert.get(type));
069    }
070
071    public static <K> StringConvert<OrderedSet<K>> convertOrderedSet(final Class<K> type) {
072        return convertOrderedSet((StringConvert<K>) StringConvert.get(type.getSimpleName()));
073    }
074    public static <K> StringConvert<UnorderedSet<K>> convertUnorderedSet(final StringConvert<K> convert) {
075        CharSequence[] types = StringConvert.asArray("UnorderedSet", convert.name);
076        StringConvert found = StringConvert.lookup(types);
077        if (found != null)
078            return found; // in this case we've already created a StringConvert for this type combination
079
080        return new StringConvert<UnorderedSet<K>>(types) {
081            @Override
082            public String stringify(UnorderedSet<K> item) {
083                StringBuilder sb = new StringBuilder(100);
084                int i = 0;
085                for (K k : item) {
086                    if (item == k)
087                        return "";
088                    ObText.appendQuoted(sb, convert.stringify(k));
089                    if (++i < item.size())
090                        sb.append(' ');
091                }
092                return sb.toString();
093            }
094
095            @Override
096            public UnorderedSet<K> restore(String text) {
097                ObText.ContentMatcher m = makeMatcher(text);
098                UnorderedSet<K> d;
099                if(convert.isArray)
100                    d = new UnorderedSet<>(CrossHash.generalHasher);
101                else
102                    d = new UnorderedSet<>();
103                while (m.find()) {
104                    if (m.hasMatch()) {
105                        d.add(convert.restore(m.getMatch()));
106                    }
107                }
108                return d;
109            }
110        };
111    }
112
113    public static <K> StringConvert<UnorderedSet<K>> convertUnorderedSet(final CharSequence type) {
114        return convertUnorderedSet((StringConvert<K>) StringConvert.get(type));
115    }
116
117    public static <K> StringConvert<UnorderedSet<K>> convertUnorderedSet(final Class<K> type) {
118        return convertUnorderedSet((StringConvert<K>) StringConvert.get(type.getSimpleName()));
119    }
120
121    public static <K, V> StringConvert<OrderedMap<K, V>> convertOrderedMap(final StringConvert<K> convertK, final StringConvert<V> convertV) {
122        CharSequence[] types = StringConvert.asArray("OrderedMap", convertK.name, convertV.name);
123        StringConvert found = StringConvert.lookup(types);
124        if (found != null)
125            return found; // in this case we've already created a StringConvert for this type combination
126
127        return new StringConvert<OrderedMap<K, V>>(types) {
128            @Override
129            public String stringify(OrderedMap<K, V> item) {
130                StringBuilder sb = new StringBuilder(100);
131                K k;
132                V v;
133                for (int i = 0; i < item.size(); ) {
134                    k = item.keyAt(i);
135                    if (k == item)
136                        return "";
137                    appendQuoted(sb, convertK.stringify(k));
138                    sb.append(' ');
139                    v = item.getAt(i);
140                    if (v == item)
141                        return "";
142                    appendQuoted(sb, convertV.stringify(v));
143                    if (++i < item.size())
144                        sb.append(' ');
145                }
146                return sb.toString();
147            }
148
149            @Override
150            public OrderedMap<K, V> restore(String text) {
151                ObText.ContentMatcher m = makeMatcher(text);
152                OrderedMap<K, V> d;
153                if(convertK.isArray)
154                    d = new OrderedMap<>(CrossHash.generalHasher);
155                else
156                    d = new OrderedMap<>();
157                String t;
158                while (m.find()) {
159                    if (m.hasMatch()) {
160                        t = m.getMatch();
161                        if (m.find() && m.hasMatch()) {
162                            d.put(convertK.restore(t), convertV.restore(m.getMatch()));
163                        }
164                    }
165                }
166                return d;
167            }
168        };
169    }
170
171    public static <K, V> StringConvert<OrderedMap<K, V>> convertOrderedMap(final CharSequence typeK, final CharSequence typeV) {
172        return convertOrderedMap((StringConvert<K>) StringConvert.get(typeK), (StringConvert<V>) StringConvert.get(typeV));
173    }
174
175    public static <K, V> StringConvert<OrderedMap<K, V>> convertOrderedMap(final Class<K> typeK, final Class<V> typeV) {
176        return convertOrderedMap((StringConvert<K>) StringConvert.get(typeK.getSimpleName()),
177                (StringConvert<V>) StringConvert.get(typeV.getSimpleName()));
178    }
179
180    public static <K, V> StringConvert<UnorderedMap<K, V>> convertUnorderedMap(final StringConvert<K> convertK, final StringConvert<V> convertV) {
181        CharSequence[] types = StringConvert.asArray("UnorderedMap", convertK.name, convertV.name);
182        StringConvert found = StringConvert.lookup(types);
183        if (found != null)
184            return found; // in this case we've already created a StringConvert for this type combination
185
186        return new StringConvert<UnorderedMap<K, V>>(types) {
187            @Override
188            public String stringify(UnorderedMap<K, V> item) {
189                StringBuilder sb = new StringBuilder(100);
190                K k;
191                V v;
192                Iterator<Map.Entry<K, V>> it = item.entrySet().iterator();
193                Map.Entry<K, V> ent;
194                while (it.hasNext())
195                {
196                    ent = it.next();
197                    k = ent.getKey();
198                    if (k == item)
199                        return "";
200                    appendQuoted(sb, convertK.stringify(k));
201                    sb.append(' ');
202                    v = ent.getValue();
203                    if (v == item)
204                        return "";
205                    appendQuoted(sb, convertV.stringify(v));
206                    if (it.hasNext())
207                        sb.append(' ');
208                }
209                return sb.toString();
210            }
211
212            @Override
213            public UnorderedMap<K, V> restore(String text) {
214                ObText.ContentMatcher m = makeMatcher(text);
215                UnorderedMap<K, V> d;
216                if(convertK.isArray)
217                    d = new UnorderedMap<>(CrossHash.generalHasher);
218                else
219                    d = new UnorderedMap<>();
220                String t;
221                while (m.find()) {
222                    if (m.hasMatch()) {
223                        t = m.getMatch();
224                        if (m.find() && m.hasMatch()) {
225                            d.put(convertK.restore(t), convertV.restore(m.getMatch()));
226                        }
227                    }
228                }
229                return d;
230            }
231        };
232    }
233
234    public static <K, V> StringConvert<UnorderedMap<K, V>> convertUnorderedMap(final CharSequence typeK, final CharSequence typeV) {
235        return convertUnorderedMap((StringConvert<K>) StringConvert.get(typeK), (StringConvert<V>) StringConvert.get(typeV));
236    }
237
238    public static <K, V> StringConvert<UnorderedMap<K, V>> convertUnorderedMap(final Class<K> typeK, final Class<V> typeV) {
239        return convertUnorderedMap((StringConvert<K>) StringConvert.get(typeK.getSimpleName()),
240                (StringConvert<V>) StringConvert.get(typeV.getSimpleName()));
241    }
242
243    public static <K> StringConvert<HashSet<K>> convertHashSet(final StringConvert<K> convert) {
244        CharSequence[] types = StringConvert.asArray("HashSet", convert.name);
245        StringConvert found = StringConvert.lookup(types);
246        if (found != null)
247            return found; // in this case we've already created a StringConvert for this type combination
248
249        return new StringConvert<HashSet<K>>(types) {
250            @Override
251            public String stringify(HashSet<K> item) {
252                StringBuilder sb = new StringBuilder(100);
253                Iterator<K> it = item.iterator();
254                K k;
255                while (it.hasNext()){
256                    k = it.next();
257                    if (item == k)
258                        return "";
259                    ObText.appendQuoted(sb, convert.stringify(k));
260                    if (it.hasNext())
261                        sb.append(' ');
262                }
263                return sb.toString();
264            }
265
266            @Override
267            public HashSet<K> restore(String text) {
268                ObText.ContentMatcher m = makeMatcher(text);
269                HashSet<K> d = new HashSet<>();
270                while (m.find()) {
271                    if (m.hasMatch()) {
272                        d.add(convert.restore(m.getMatch()));
273                    }
274                }
275                return d;
276            }
277        };
278    }
279
280    public static <K> StringConvert<HashSet<K>> convertHashSet(final CharSequence type) {
281        return convertHashSet((StringConvert<K>) StringConvert.get(type));
282    }
283
284    public static <K> StringConvert<HashSet<K>> convertHashSet(final Class<K> type) {
285        return convertHashSet((StringConvert<K>) StringConvert.get(type.getSimpleName()));
286    }
287
288    public static <K, V> StringConvert<HashMap<K, V>> convertHashMap(final StringConvert<K> convertK, final StringConvert<V> convertV) {
289        CharSequence[] types = StringConvert.asArray("HashMap", convertK.name, convertV.name);
290        StringConvert found = StringConvert.lookup(types);
291        if (found != null)
292            return found; // in this case we've already created a StringConvert for this type combination
293
294        return new StringConvert<HashMap<K, V>>(types) {
295            @Override
296            public String stringify(HashMap<K, V> item) {
297                StringBuilder sb = new StringBuilder(100);
298                K k;
299                V v;
300                Iterator<K> kit = item.keySet().iterator();
301                Iterator<V> vit = item.values().iterator();
302                while (kit.hasNext()) {
303                    k = kit.next();
304                    if (k == item)
305                        return "";
306                    appendQuoted(sb, convertK.stringify(k));
307                    sb.append(' ');
308                    v = vit.next();
309                    if (v == item)
310                        return "";
311                    appendQuoted(sb, convertV.stringify(v));
312                    if (kit.hasNext())
313                        sb.append(' ');
314                }
315                return sb.toString();
316            }
317
318            @Override
319            public HashMap<K, V> restore(String text) {
320                ObText.ContentMatcher m = makeMatcher(text);
321                HashMap<K, V> d = new HashMap<>();
322                String t;
323                while (m.find()) {
324                    if (m.hasMatch()) {
325                        t = m.getMatch();
326                        if (m.find() && m.hasMatch()) {
327                            d.put(convertK.restore(t), convertV.restore(m.getMatch()));
328                        }
329                    }
330                }
331                return d;
332            }
333        };
334    }
335
336    public static <K, V> StringConvert<HashMap<K, V>> convertHashMap(final CharSequence typeK, final CharSequence typeV) {
337        return convertHashMap((StringConvert<K>) StringConvert.get(typeK), (StringConvert<V>) StringConvert.get(typeV));
338    }
339
340    public static <K, V> StringConvert<HashMap<K, V>> convertHashMap(final Class<K> typeK, final Class<V> typeV) {
341        return convertHashMap((StringConvert<K>) StringConvert.get(typeK.getSimpleName()),
342                (StringConvert<V>) StringConvert.get(typeV.getSimpleName()));
343    }
344
345    public static <K> StringConvert<Arrangement<K>> convertArrangement(final StringConvert<K> convert) {
346        CharSequence[] types = StringConvert.asArray("Arrangement", convert.name);
347        StringConvert found = StringConvert.lookup(types);
348        if (found != null)
349            return found; // in this case we've already created a StringConvert for this type combination
350
351        return new StringConvert<Arrangement<K>>(types) {
352            @Override
353            public String stringify(Arrangement<K> item) {
354                StringBuilder sb = new StringBuilder(100);
355                K k;
356                for (int i = 0; i < item.size(); ) {
357                    k = item.keyAt(i);
358                    if (item == k)
359                        return "";
360                    ObText.appendQuoted(sb, convert.stringify(k));
361                    if (++i < item.size())
362                        sb.append(' ');
363                }
364                return sb.toString();
365            }
366
367            @Override
368            public Arrangement<K> restore(String text) {
369                ObText.ContentMatcher m = makeMatcher(text);
370                Arrangement<K> d;
371                if(convert.isArray)
372                    d = new Arrangement<>(CrossHash.generalHasher);
373                else
374                    d = new Arrangement<>();
375                while (m.find()) {
376                    if (m.hasMatch()) {
377                        d.add(convert.restore(m.getMatch()));
378                    }
379                }
380                return d;
381            }
382        };
383    }
384
385    public static <K> StringConvert<Arrangement<K>> convertArrangement(final CharSequence type) {
386        return convertArrangement((StringConvert<K>) StringConvert.get(type));
387    }
388
389    public static <K> StringConvert<Arrangement<K>> convertArrangement(final Class<K> type) {
390        return convertArrangement((StringConvert<K>) StringConvert.get(type.getSimpleName()));
391    }
392
393
394    public static <K> StringConvert<ArrayList<K>> convertArrayList(final StringConvert<K> convert) {
395        CharSequence[] types = StringConvert.asArray("ArrayList", convert.name);
396        StringConvert found = StringConvert.lookup(types);
397        if (found != null)
398            return found; // in this case we've already created a StringConvert for this type combination
399        return new StringConvert<ArrayList<K>>(types) {
400            @Override
401            public String stringify(ArrayList<K> item) {
402                StringBuilder sb = new StringBuilder(100);
403                K k;
404                for (int i = 0; i < item.size(); ) {
405                    k = item.get(i);
406                    if (item == k)
407                        return "";
408                    appendQuoted(sb, convert.stringify(k));
409                    if (++i < item.size())
410                        sb.append(' ');
411                }
412                return sb.toString();
413            }
414
415            @Override
416            public ArrayList<K> restore(String text) {
417                ObText.ContentMatcher m = makeMatcher(text);
418                ArrayList<K> d = new ArrayList<>();
419                while (m.find()) {
420                    if (m.hasMatch()) {
421                        d.add(convert.restore(m.getMatch()));
422                    }
423                }
424                return d;
425            }
426        };
427    }
428
429    public static <K> StringConvert<ArrayList<K>> convertArrayList(final CharSequence type) {
430        return convertArrayList((StringConvert<K>) StringConvert.get(type));
431    }
432
433    public static <K> StringConvert<ArrayList<K>> convertArrayList(final Class<K> type) {
434        return convertArrayList((StringConvert<K>) StringConvert.get(type.getSimpleName()));
435    }
436    public static <K> StringConvert<List<K>> convertList(final StringConvert<K> convert) {
437        CharSequence[] types = StringConvert.asArray("List", convert.name);
438        StringConvert found = StringConvert.lookup(types);
439        if (found != null)
440            return found; // in this case we've already created a StringConvert for this type combination
441        return new StringConvert<List<K>>(types) {
442            @Override
443            public String stringify(List<K> item) {
444                StringBuilder sb = new StringBuilder(100);
445                K k;
446                for (int i = 0; i < item.size(); ) {
447                    k = item.get(i);
448                    if (item == k)
449                        return "";
450                    appendQuoted(sb, convert.stringify(k));
451                    if (++i < item.size())
452                        sb.append(' ');
453                }
454                return sb.toString();
455            }
456
457            @Override
458            public ArrayList<K> restore(String text) {
459                ObText.ContentMatcher m = makeMatcher(text);
460                ArrayList<K> d = new ArrayList<>();
461                while (m.find()) {
462                    if (m.hasMatch()) {
463                        d.add(convert.restore(m.getMatch()));
464                    }
465                }
466                return d;
467            }
468        };
469    }
470
471    public static <K> StringConvert<List<K>> convertList(final CharSequence type) {
472        return convertList((StringConvert<K>) StringConvert.get(type));
473    }
474
475    public static <K> StringConvert<List<K>> convertList(final Class<K> type) {
476        return convertList((StringConvert<K>) StringConvert.get(type.getSimpleName()));
477    }
478
479    public static final StringConvert<Coord> convertCoord = new StringConvert<Coord>("Coord") {
480        @Override
481        public String stringify(Coord item) {
482            if(item == null) return "n";
483            return item.x + "," + item.y;
484        }
485
486        @Override
487        public Coord restore(String text) {
488            if(text == null || text.equals("n")) return null;
489            return Coord.get(StringKit.intFromDec(text), StringKit.intFromDec(text, text.indexOf(',') + 1, text.length()));
490        }
491    };
492
493    public static final StringConvert<Coord[]> convertArrayCoord = new StringConvert<Coord[]>(true,"Coord[]") {
494        @Override
495        public String stringify(Coord[] item) {
496            if(item == null)
497                return "N";
498            int len = item.length;
499            StringBuilder sb = new StringBuilder(len * 5);
500            for (int i = 0; i < len; ) {
501                if(item[i] == null)
502                    sb.append('n');
503                else
504                    sb.append(item[i].x).append(',').append(item[i].y);
505                if (++i < len)
506                    sb.append(';');
507            }
508            return sb.toString();
509        }
510
511        @Override
512        public Coord[] restore(String text) {
513            if(text == null || text.equals("N"))
514                return null;
515            Coord[] coords = new Coord[StringKit.count(text, ';') + 1];
516            int start = -1, end = text.indexOf(',');
517            for (int i = 0; i < coords.length; i++) {
518                if(text.charAt(start+1) == 'n') {
519                    coords[i] = null;
520                    start = text.indexOf(';', end + 1);
521                }
522                else
523                    coords[i] = Coord.get(StringKit.intFromDec(text, start + 1, end),
524                        StringKit.intFromDec(text, end + 1, (start = text.indexOf(';', end + 1))));
525                end = text.indexOf(',', start + 1);
526            }
527            return coords;
528        }
529    };
530
531    public static final StringConvert<GreasedRegion> convertGreasedRegion = new StringConvert<GreasedRegion>("GreasedRegion") {
532        @Override
533        public String stringify(GreasedRegion item) {
534            return item.serializeToString();
535        }
536
537        @Override
538        public GreasedRegion restore(String text) {
539            return GreasedRegion.deserializeFromString(text);
540        }
541    };
542    public static final StringConvert<IntVLA> convertIntVLA = new StringConvert<IntVLA>("IntVLA") {
543        @Override
544        public String stringify(IntVLA item) {
545            return item.toString(",");
546        }
547
548        @Override
549        public IntVLA restore(String text) {
550            return IntVLA.deserializeFromString(text);
551        }
552    };
553
554    public static final StringConvert<FakeLanguageGen> convertFakeLanguageGen = new StringConvert<FakeLanguageGen>("FakeLanguageGen")
555    {
556        @Override
557        public String stringify(FakeLanguageGen item) {
558            return item.serializeToString();
559        }
560
561        @Override
562        public FakeLanguageGen restore(String text) {
563            return FakeLanguageGen.deserializeFromString(text);
564        }
565    };
566
567    public static final StringConvert<FakeLanguageGen.SentenceForm> convertSentenceForm = new StringConvert<FakeLanguageGen.SentenceForm>("FakeLanguageGen$SentenceForm")
568    {
569        @Override
570        public String stringify(FakeLanguageGen.SentenceForm item) {
571            return item.serializeToString();
572        }
573
574        @Override
575        public FakeLanguageGen.SentenceForm restore(String text) {
576            return FakeLanguageGen.SentenceForm.deserializeFromString(text);
577        }
578    };
579
580    public static final StringConvert<MarkovTextLimited> convertMarkovText = new StringConvert<MarkovTextLimited>("MarkovTextLimited")
581    {
582        @Override
583        public String stringify(MarkovTextLimited item) {
584            return item.serializeToString();
585        }
586
587        @Override
588        public MarkovTextLimited restore(String text) {
589            return MarkovTextLimited.deserializeFromString(text);
590        }
591    };
592
593    public static final StringConvert<ObText> convertObText = new StringConvert<ObText>("ObText") {
594        @Override
595        public String stringify(ObText item) {
596            return item.serializeToString();
597        }
598
599        @Override
600        public ObText restore(String text) {
601            return ObText.deserializeFromString(text);
602        }
603    };
604
605    public static final StringConvert<WeightedTable> convertWeightedTable = new StringConvert<WeightedTable>("WeightedTable") {
606        @Override
607        public String stringify(WeightedTable item) {
608            return item.serializeToString();
609        }
610
611        @Override
612        public WeightedTable restore(String text) {
613            return WeightedTable.deserializeFromString(text);
614        }
615    };
616
617
618
619    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
620    /////// CORE JDK TYPES, PRIMITIVES, AND PRIMITIVE ARRAYS ARE THE ONLY TYPES AFTER THIS POINT
621    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
622
623
624    /**
625     * Simple implementation to help when passing StringConverts around with data that is already a String.
626     */
627    public static final StringConvert<String> convertString = new StringConvert<String>("String") {
628        @Override
629        public String stringify(String item) {
630            return item;
631        }
632
633        @Override
634        public String restore(String text) {
635            return text;
636        }
637    };
638
639    public static final StringConvert<Boolean> convertBoolean = new StringConvert<Boolean>("Boolean") {
640        @Override
641        public String stringify(Boolean item) {
642            return item == null ? "n" : item ? "1" : "0";
643        }
644
645        @Override
646        public Boolean restore(String text) {
647            char c;
648            return (text == null || text.isEmpty() || (c = text.charAt(0)) == 'n') ? null : c == '1';
649        }
650    };
651
652    public static final StringConvert<Byte> convertByte = new StringConvert<Byte>("Byte") {
653        @Override
654        public String stringify(Byte item) {
655            return item.toString();
656        }
657
658        @Override
659        public Byte restore(String text) {
660            return Byte.decode(text);
661        }
662    };
663
664    public static final StringConvert<Short> convertShort = new StringConvert<Short>("Short") {
665        @Override
666        public String stringify(Short item) {
667            return item.toString();
668        }
669
670        @Override
671        public Short restore(String text) {
672            return Short.decode(text);
673        }
674    };
675
676    public static final StringConvert<Integer> convertInt = new StringConvert<Integer>("Integer") {
677        @Override
678        public String stringify(Integer item) {
679            return item.toString();
680        }
681
682        @Override
683        public Integer restore(String text) {
684            return Integer.decode(text);
685        }
686    };
687
688    public static final StringConvert<Long> convertLong = new StringConvert<Long>("Long") {
689        @Override
690        public String stringify(Long item) {
691            return item.toString();
692        }
693
694        @Override
695        public Long restore(String text) {
696            return Long.decode(text);
697        }
698    };
699
700    public static final StringConvert<Float> convertFloat = new StringConvert<Float>("Float") {
701        @Override
702        public String stringify(Float item) {
703            return item.toString();
704        }
705
706        @Override
707        public Float restore(String text) {
708            return Float.parseFloat(text);
709        }
710    };
711
712    public static final StringConvert<Double> convertDouble = new StringConvert<Double>("Double") {
713        @Override
714        public String stringify(Double item) {
715            return item.toString();
716        }
717
718        @Override
719        public Double restore(String text) {
720            return Double.parseDouble(text);
721        }
722    };
723
724    public static final StringConvert<Character> convertChar = new StringConvert<Character>("Character") {
725        @Override
726        public String stringify(Character item) {
727            return item.toString();
728        }
729
730        @Override
731        public Character restore(String text) {
732            return text.charAt(0);
733        }
734    };
735
736    public static final StringConvert<boolean[]> convertArrayBoolean = new StringConvert<boolean[]>(true,"boolean[]") {
737        @Override
738        public String stringify(boolean[] item) {
739            return StringKit.joinAlt(item);
740        }
741
742        @Override
743        public boolean[] restore(String text) {
744            if(text == null || text.equals("N")) return null;
745            int amount = text.length();
746            if (amount <= 0) return new boolean[0];
747            boolean[] splat = new boolean[amount];
748            for (int i = 0; i < amount; i++) {
749                splat[amount] = text.charAt(i) == '1';
750            }
751            return splat;
752        }
753    };
754
755    public static final StringConvert<byte[]> convertArrayByte = new StringConvert<byte[]>(true,"byte[]") {
756        @Override
757        public String stringify(byte[] item) {
758            if(item == null) return "N";
759            return StringKit.join(",", item);
760        }
761
762        @Override
763        public byte[] restore(String text) {
764            if(text == null || text.equals("N")) return null;
765            int amount = StringKit.count(text, ",");
766            if (amount <= 0) return new byte[]{Byte.decode(text)};
767            byte[] splat = new byte[amount + 1];
768            int dl = 1, idx = -dl, idx2;
769            for (int i = 0; i < amount; i++) {
770                splat[i] = Byte.decode(StringKit.safeSubstring(text, idx + dl, idx = text.indexOf(',', idx + dl)));
771            }
772            if ((idx2 = text.indexOf(',', idx + dl)) < 0) {
773                splat[amount] = Byte.decode(StringKit.safeSubstring(text, idx + dl, text.length()));
774            } else {
775                splat[amount] = Byte.decode(StringKit.safeSubstring(text, idx + dl, idx2));
776            }
777            return splat;
778        }
779    };
780
781
782    public static final StringConvert<short[]> convertArrayShort = new StringConvert<short[]>(true,"short[]") {
783        @Override
784        public String stringify(short[] item) {
785            if(item == null) return "N";
786            return StringKit.join(",", item);
787        }
788
789        @Override
790        public short[] restore(String text) {
791            if(text == null || text.equals("N")) return null;
792            int amount = StringKit.count(text, ",");
793            if (amount <= 0) return new short[]{Short.decode(text)};
794            short[] splat = new short[amount + 1];
795            int dl = 1, idx = -dl, idx2;
796            for (int i = 0; i < amount; i++) {
797                splat[i] = Short.decode(StringKit.safeSubstring(text, idx + dl, idx = text.indexOf(',', idx + dl)));
798            }
799            if ((idx2 = text.indexOf(',', idx + dl)) < 0) {
800                splat[amount] = Short.decode(StringKit.safeSubstring(text, idx + dl, text.length()));
801            } else {
802                splat[amount] = Short.decode(StringKit.safeSubstring(text, idx + dl, idx2));
803            }
804            return splat;
805        }
806    };
807
808    public static final StringConvert<int[]> convertArrayInt = new StringConvert<int[]>(true,"int[]") {
809        @Override
810        public String stringify(int[] item) {
811            if(item == null) return "N";
812            return StringKit.join(",", item);
813        }
814
815        @Override
816        public int[] restore(String text) {
817            if(text == null || text.equals("N")) return null;
818            int amount = StringKit.count(text, ",");
819            if (amount <= 0) return new int[]{Integer.decode(text)};
820            int[] splat = new int[amount + 1];
821            int dl = 1, idx = -dl, idx2;
822            for (int i = 0; i < amount; i++) {
823                splat[i] = Integer.decode(StringKit.safeSubstring(text, idx + dl, idx = text.indexOf(',', idx + dl)));
824            }
825            if ((idx2 = text.indexOf(',', idx + dl)) < 0) {
826                splat[amount] = Integer.decode(StringKit.safeSubstring(text, idx + dl, text.length()));
827            } else {
828                splat[amount] = Integer.decode(StringKit.safeSubstring(text, idx + dl, idx2));
829            }
830            return splat;
831        }
832    };
833
834
835    public static final StringConvert<long[]> convertArrayLong = new StringConvert<long[]>(true,"long[]") {
836        @Override
837        public String stringify(long[] item) {
838            if(item == null) return "N";
839            return StringKit.join(",", item);
840        }
841
842        @Override
843        public long[] restore(String text) {
844            if(text == null || text.equals("N")) return null;
845            int amount = StringKit.count(text, ",");
846            if (amount <= 0) return new long[]{Long.decode(text)};
847            long[] splat = new long[amount + 1];
848            int dl = 1, idx = -dl, idx2;
849            for (int i = 0; i < amount; i++) {
850                splat[i] = Long.decode(StringKit.safeSubstring(text, idx + dl, idx = text.indexOf(',', idx + dl)));
851            }
852            if ((idx2 = text.indexOf(',', idx + dl)) < 0) {
853                splat[amount] = Long.decode(StringKit.safeSubstring(text, idx + dl, text.length()));
854            } else {
855                splat[amount] = Long.decode(StringKit.safeSubstring(text, idx + dl, idx2));
856            }
857            return splat;
858        }
859    };
860
861    public static final StringConvert<float[]> convertArrayFloat = new StringConvert<float[]>(true,"float[]") {
862        @Override
863        public String stringify(float[] item) {
864            if(item == null) return "N";
865            return StringKit.join(",", item);
866        }
867
868        @Override
869        public float[] restore(String text) {
870            if(text == null || text.equals("N")) return null;
871            int amount = StringKit.count(text, ",");
872            if (amount <= 0) return new float[]{Float.parseFloat(text)};
873            float[] splat = new float[amount + 1];
874            int dl = 1, idx = -dl, idx2;
875            for (int i = 0; i < amount; i++) {
876                splat[i] = Float.parseFloat(StringKit.safeSubstring(text, idx + dl, idx = text.indexOf(',', idx + dl)));
877            }
878            if ((idx2 = text.indexOf(',', idx + dl)) < 0) {
879                splat[amount] = Float.parseFloat(StringKit.safeSubstring(text, idx + dl, text.length()));
880            } else {
881                splat[amount] = Float.parseFloat(StringKit.safeSubstring(text, idx + dl, idx2));
882            }
883            return splat;
884        }
885    };
886
887    public static final StringConvert<double[]> convertArrayDouble = new StringConvert<double[]>(true,"double[]") {
888        @Override
889        public String stringify(double[] item) {
890            if(item == null) return "N";
891            return StringKit.join(",", item);
892        }
893
894        @Override
895        public double[] restore(String text) {
896            if(text == null || text.equals("N")) return null;
897            int amount = StringKit.count(text, ",");
898            if (amount <= 0) return new double[]{Double.parseDouble(text)};
899            double[] splat = new double[amount + 1];
900            int dl = 1, idx = -dl, idx2;
901            for (int i = 0; i < amount; i++) {
902                splat[i] = Double.parseDouble(StringKit.safeSubstring(text, idx + dl, idx = text.indexOf(',', idx + dl)));
903            }
904            if ((idx2 = text.indexOf(',', idx + dl)) < 0) {
905                splat[amount] = Double.parseDouble(StringKit.safeSubstring(text, idx + dl, text.length()));
906            } else {
907                splat[amount] = Double.parseDouble(StringKit.safeSubstring(text, idx + dl, idx2));
908            }
909            return splat;
910        }
911    };
912
913
914    public static final StringConvert<char[]> convertArrayChar = new StringConvert<char[]>(true,"char[]") {
915        @Override
916        public String stringify(char[] item) {
917            if(item == null) return "";
918            return String.valueOf(item);
919        }
920
921        @Override
922        public char[] restore(String text) {
923            return text.toCharArray();
924        }
925    };
926
927    public static final StringConvert<boolean[][]> convertArrayBoolean2D = new StringConvert<boolean[][]>(true, "boolean[][]") {
928        @Override
929        public String stringify(boolean[][] item) {
930            if(item == null)
931                return "N";
932            int len;
933            if((len = item.length) <= 0)
934                return "";
935            StringBuilder sb = new StringBuilder(len * 128);
936            if(item[0] == null)
937                sb.append('n');
938            else
939                sb.append(StringKit.joinAlt(item[0]));
940            for (int i = 1; i < len; i++) {
941                if(item[i] == null)
942                    sb.append(";n");
943                else
944                    sb.append(';').append(StringKit.joinAlt(item[i]));
945            }
946            return sb.toString();
947        }
948
949        @Override
950        public boolean[][] restore(String text) {
951            if(text == null) return null;
952            int len;
953            if((len = text.length()) <= 0) return new boolean[0][0];
954            if(text.charAt(0) == 'N') return null;
955            int width = StringKit.count(text, ';')+1;
956            boolean[][] val = new boolean[width][];
957            int start = 0, end = text.indexOf(';');
958            for (int i = 0; i < width; i++) {
959                if(start == end || start >= len) val[i] = new boolean[0];
960                else if(text.charAt(start) == 'n') val[i] = null;
961                else {
962                    int amount = end - start;
963                    val[i] = new boolean[amount];
964                    for (int j = 0; j < amount; j++) {
965                        val[i][j] = text.charAt(start + j) == '1';
966                    }
967                }
968                start = end+1;
969                end = text.indexOf(';', start);
970            }
971            return val;
972        }
973    };
974
975    public static final StringConvert<byte[][]> convertArrayByte2D = new StringConvert<byte[][]>(true, "byte[][]") {
976        @Override
977        public String stringify(byte[][] item) {
978            if(item == null)
979                return "N";
980            int len;
981            if((len = item.length) <= 0)
982                return "";
983            StringBuilder sb = new StringBuilder(len * 128);
984            if(item[0] == null)
985                sb.append('n');
986            else
987                sb.append(StringKit.join(",", item[0]));
988            for (int i = 1; i < len; i++) {
989                if(item[i] == null)
990                    sb.append(";n");
991                else
992                    sb.append(';').append(StringKit.join(",", item[i]));
993            }
994            return sb.toString();
995        }
996
997        @Override
998        public byte[][] restore(String text) {
999            if(text == null) return null;
1000            int len;
1001            if((len = text.length()) <= 0) return new byte[0][0];
1002            if(text.charAt(0) == 'N') return null;
1003            int width = StringKit.count(text, ';')+1;
1004            byte[][] val = new byte[width][];
1005            int start = 0, end = text.indexOf(';');
1006            for (int i = 0; i < width; i++) {
1007                if(start == end || start >= len) val[i] = new byte[0];
1008                else if(text.charAt(start) == 'n') val[i] = null;
1009                else {
1010                    int amount = StringKit.count(text, ",", start, end);
1011                    if (amount <= 0){
1012                        val[i] = new byte[]{Byte.decode(text)};
1013                        continue;
1014                    }
1015                    val[i] = new byte[amount + 1];
1016                    int dl = 1, idx = start - dl, idx2;
1017                    for (int j = 0; j < amount; j++) {
1018                        val[i][j] = Byte.decode(StringKit.safeSubstring(text, idx + dl, idx = text.indexOf(',', idx + dl)));
1019                    }
1020                    if ((idx2 = text.indexOf(',', idx + dl)) < 0) {
1021                        val[i][amount] = Byte.decode(StringKit.safeSubstring(text, idx + dl, text.length()));
1022                    } else if(idx2 < end){
1023                        val[i][amount] = Byte.decode(StringKit.safeSubstring(text, idx + dl, idx2));
1024                    } else {
1025                        val[i][amount] = Byte.decode(StringKit.safeSubstring(text, idx + dl, end));
1026                    }
1027                }
1028                start = end+1;
1029                end = text.indexOf(';', start);
1030            }
1031            return val;
1032        }
1033    };
1034
1035
1036    public static final StringConvert<short[][]> convertArrayShort2D = new StringConvert<short[][]>(true, "short[][]") {
1037        @Override
1038        public String stringify(short[][] item) {
1039            if(item == null)
1040                return "N";
1041            int len;
1042            if((len = item.length) <= 0)
1043                return "";
1044            StringBuilder sb = new StringBuilder(len * 128);
1045            if(item[0] == null)
1046                sb.append('n');
1047            else
1048                sb.append(StringKit.join(",", item[0]));
1049            for (int i = 1; i < len; i++) {
1050                if(item[i] == null)
1051                    sb.append(";n");
1052                else
1053                    sb.append(';').append(StringKit.join(",", item[i]));
1054            }
1055            return sb.toString();
1056        }
1057
1058        @Override
1059        public short[][] restore(String text) {
1060            if(text == null) return null;
1061            int len;
1062            if((len = text.length()) <= 0) return new short[0][0];
1063            if(text.charAt(0) == 'N') return null;
1064            int width = StringKit.count(text, ';')+1;
1065            short[][] val = new short[width][];
1066            int start = 0, end = text.indexOf(';');
1067            for (int i = 0; i < width; i++) {
1068                if(start == end || start >= len) val[i] = new short[0];
1069                else if(text.charAt(start) == 'n') val[i] = null;
1070                else {
1071                    int amount = StringKit.count(text, ",", start, end);
1072                    if (amount <= 0){
1073                        val[i] = new short[]{Short.decode(text)};
1074                        continue;
1075                    }
1076                    val[i] = new short[amount + 1];
1077                    int dl = 1, idx = start - dl, idx2;
1078                    for (int j = 0; j < amount; j++) {
1079                        val[i][j] = Short.decode(StringKit.safeSubstring(text, idx + dl, idx = text.indexOf(',', idx + dl)));
1080                    }
1081                    if ((idx2 = text.indexOf(',', idx + dl)) < 0) {
1082                        val[i][amount] = Short.decode(StringKit.safeSubstring(text, idx + dl, text.length()));
1083                    } else if(idx2 < end){
1084                        val[i][amount] = Short.decode(StringKit.safeSubstring(text, idx + dl, idx2));
1085                    } else {
1086                        val[i][amount] = Short.decode(StringKit.safeSubstring(text, idx + dl, end));
1087                    }
1088                }
1089                start = end+1;
1090                end = text.indexOf(';', start);
1091            }
1092            return val;
1093        }
1094    };
1095
1096    public static final StringConvert<int[][]> convertArrayInt2D = new StringConvert<int[][]>(true, "int[][]") {
1097        @Override
1098        public String stringify(int[][] item) {
1099            if(item == null)
1100                return "N";
1101            int len;
1102            if((len = item.length) <= 0)
1103                return "";
1104            StringBuilder sb = new StringBuilder(len * 128);
1105            if(item[0] == null)
1106                sb.append('n');
1107            else
1108                sb.append(StringKit.join(",", item[0]));
1109            for (int i = 1; i < len; i++) {
1110                if(item[i] == null)
1111                    sb.append(";n");
1112                else
1113                    sb.append(';').append(StringKit.join(",", item[i]));
1114            }
1115            return sb.toString();
1116        }
1117
1118        @Override
1119        public int[][] restore(String text) {
1120            if(text == null) return null;
1121            int len;
1122            if((len = text.length()) <= 0) return new int[0][0];
1123            if(text.charAt(0) == 'N') return null;
1124            int width = StringKit.count(text, ';')+1;
1125            int[][] val = new int[width][];
1126            int start = 0, end = text.indexOf(';');
1127            for (int i = 0; i < width; i++) {
1128                if(start == end || start >= len) val[i] = new int[0];
1129                else if(text.charAt(start) == 'n') val[i] = null;
1130                else {
1131                    int amount = StringKit.count(text, ",", start, end);
1132                    if (amount <= 0){
1133                        val[i] = new int[]{Integer.decode(text)};
1134                        continue;
1135                    }
1136                    val[i] = new int[amount + 1];
1137                    int dl = 1, idx = start - dl, idx2;
1138                    for (int j = 0; j < amount; j++) {
1139                        val[i][j] = Integer.decode(StringKit.safeSubstring(text, idx + dl, idx = text.indexOf(',', idx + dl)));
1140                    }
1141                    if ((idx2 = text.indexOf(',', idx + dl)) < 0) {
1142                        val[i][amount] = Integer.decode(StringKit.safeSubstring(text, idx + dl, text.length()));
1143                    } else if(idx2 < end){
1144                        val[i][amount] = Integer.decode(StringKit.safeSubstring(text, idx + dl, idx2));
1145                    } else {
1146                        val[i][amount] = Integer.decode(StringKit.safeSubstring(text, idx + dl, end));
1147                    }
1148                }
1149                start = end+1;
1150                end = text.indexOf(';', start);
1151            }
1152            return val;
1153        }
1154    };
1155
1156    public static final StringConvert<long[][]> convertArrayLong2D = new StringConvert<long[][]>(true, "long[][]") {
1157        @Override
1158        public String stringify(long[][] item) {
1159            if(item == null)
1160                return "N";
1161            int len;
1162            if((len = item.length) <= 0)
1163                return "";
1164            StringBuilder sb = new StringBuilder(len * 128);
1165            if(item[0] == null)
1166                sb.append('n');
1167            else
1168                sb.append(StringKit.join(",", item[0]));
1169            for (int i = 1; i < len; i++) {
1170                if(item[i] == null)
1171                    sb.append(";n");
1172                else
1173                    sb.append(';').append(StringKit.join(",", item[i]));
1174            }
1175            return sb.toString();
1176        }
1177
1178        @Override
1179        public long[][] restore(String text) {
1180            if(text == null) return null;
1181            int len;
1182            if((len = text.length()) <= 0) return new long[0][0];
1183            if(text.charAt(0) == 'N') return null;
1184            int width = StringKit.count(text, ';')+1;
1185            long[][] val = new long[width][];
1186            int start = 0, end = text.indexOf(';');
1187            for (int i = 0; i < width; i++) {
1188                if(start == end || start >= len) val[i] = new long[0];
1189                else if(text.charAt(start) == 'n') val[i] = null;
1190                else {
1191                    int amount = StringKit.count(text, ",", start, end);
1192                    if (amount <= 0){
1193                        val[i] = new long[]{Long.decode(text)};
1194                        continue;
1195                    }
1196                    val[i] = new long[amount + 1];
1197                    int dl = 1, idx = start - dl, idx2;
1198                    for (int j = 0; j < amount; j++) {
1199                        val[i][j] = Long.decode(StringKit.safeSubstring(text, idx + dl, idx = text.indexOf(',', idx + dl)));
1200                    }
1201                    if ((idx2 = text.indexOf(',', idx + dl)) < 0) {
1202                        val[i][amount] = Long.decode(StringKit.safeSubstring(text, idx + dl, text.length()));
1203                    } else if(idx2 < end){
1204                        val[i][amount] = Long.decode(StringKit.safeSubstring(text, idx + dl, idx2));
1205                    } else {
1206                        val[i][amount] = Long.decode(StringKit.safeSubstring(text, idx + dl, end));
1207                    }
1208                }
1209                start = end+1;
1210                end = text.indexOf(';', start);
1211            }
1212            return val;
1213        }
1214    };
1215
1216    public static final StringConvert<float[][]> convertArrayFloat2D = new StringConvert<float[][]>(true, "float[][]") {
1217        @Override
1218        public String stringify(float[][] item) {
1219            if(item == null)
1220                return "N";
1221            int len;
1222            if((len = item.length) <= 0)
1223                return "";
1224            StringBuilder sb = new StringBuilder(len * 128);
1225            if(item[0] == null)
1226                sb.append('n');
1227            else
1228                sb.append(StringKit.join(",", item[0]));
1229            for (int i = 1; i < len; i++) {
1230                if(item[i] == null)
1231                    sb.append(";n");
1232                else
1233                    sb.append(';').append(StringKit.join(",", item[i]));
1234            }
1235            return sb.toString();
1236        }
1237
1238        @Override
1239        public float[][] restore(String text) {
1240            if(text == null) return null;
1241            int len;
1242            if((len = text.length()) <= 0) return new float[0][0];
1243            if(text.charAt(0) == 'N') return null;
1244            int width = StringKit.count(text, ';')+1;
1245            float[][] val = new float[width][];
1246            int start = 0, end = text.indexOf(';');
1247            for (int i = 0; i < width; i++) {
1248                if(start == end || start >= len) val[i] = new float[0];
1249                else if(text.charAt(start) == 'n') val[i] = null;
1250                else {
1251                    int amount = StringKit.count(text, ",", start, end);
1252                    if (amount <= 0){
1253                        val[i] = new float[]{Float.parseFloat(text)};
1254                        continue;
1255                    }
1256                    val[i] = new float[amount + 1];
1257                    int dl = 1, idx = start - dl, idx2;
1258                    for (int j = 0; j < amount; j++) {
1259                        val[i][j] = Float.parseFloat(StringKit.safeSubstring(text, idx + dl, idx = text.indexOf(',', idx + dl)));
1260                    }
1261                    if ((idx2 = text.indexOf(',', idx + dl)) < 0) {
1262                        val[i][amount] = Float.parseFloat(StringKit.safeSubstring(text, idx + dl, text.length()));
1263                    } else if(idx2 < end){
1264                        val[i][amount] = Float.parseFloat(StringKit.safeSubstring(text, idx + dl, idx2));
1265                    } else {
1266                        val[i][amount] = Float.parseFloat(StringKit.safeSubstring(text, idx + dl, end));
1267                    }
1268                }
1269                start = end+1;
1270                end = text.indexOf(';', start);
1271            }
1272            return val;
1273        }
1274    };
1275
1276    public static final StringConvert<double[][]> convertArrayDouble2D = new StringConvert<double[][]>(true, "double[][]") {
1277        @Override
1278        public String stringify(double[][] item) {
1279            if(item == null)
1280                return "N";
1281            int len;
1282            if((len = item.length) <= 0)
1283                return "";
1284            StringBuilder sb = new StringBuilder(len * 128);
1285            if(item[0] == null)
1286                sb.append('n');
1287            else
1288                sb.append(StringKit.join(",", item[0]));
1289            for (int i = 1; i < len; i++) {
1290                if(item[i] == null)
1291                    sb.append(";n");
1292                else
1293                    sb.append(';').append(StringKit.join(",", item[i]));
1294            }
1295            return sb.toString();
1296        }
1297
1298        @Override
1299        public double[][] restore(String text) {
1300            if(text == null) return null;
1301            int len;
1302            if((len = text.length()) <= 0) return new double[0][0];
1303            if(text.charAt(0) == 'N') return null;
1304            int width = StringKit.count(text, ';')+1;
1305            double[][] val = new double[width][];
1306            int start = 0, end = text.indexOf(';');
1307            for (int i = 0; i < width; i++) {
1308                if(start == end || start >= len) val[i] = new double[0];
1309                else if(text.charAt(start) == 'n') val[i] = null;
1310                else {
1311                    int amount = StringKit.count(text, ",", start, end);
1312                    if (amount <= 0){
1313                        val[i] = new double[]{Double.parseDouble(text)};
1314                        continue;
1315                    }
1316                    val[i] = new double[amount + 1];
1317                    int dl = 1, idx = start - dl, idx2;
1318                    for (int j = 0; j < amount; j++) {
1319                        val[i][j] = Double.parseDouble(StringKit.safeSubstring(text, idx + dl, idx = text.indexOf(',', idx + dl)));
1320                    }
1321                    if ((idx2 = text.indexOf(',', idx + dl)) < 0) {
1322                        val[i][amount] = Double.parseDouble(StringKit.safeSubstring(text, idx + dl, text.length()));
1323                    } else if(idx2 < end){
1324                        val[i][amount] = Double.parseDouble(StringKit.safeSubstring(text, idx + dl, idx2));
1325                    } else {
1326                        val[i][amount] = Double.parseDouble(StringKit.safeSubstring(text, idx + dl, end));
1327                    }
1328                }
1329                start = end+1;
1330                end = text.indexOf(';', start);
1331            }
1332            return val;
1333        }
1334    };
1335
1336
1337    public static final StringConvert<char[][]> convertArrayChar2D = new StringConvert<char[][]>(true, "char[][]") {
1338        @Override
1339        public String stringify(char[][] item) {
1340            int len, l2, sum;
1341            if (item == null) return "N"; // N for null
1342            if((len = item.length) <= 0) return "R0|0|";
1343            sum = l2 = item[0].length;
1344            char regular = 'R'; // R for rectangular
1345            for (int i = 1; i < len; i++) {
1346                if(item[i] == null)
1347                {
1348                    regular = 'J'; // J for jagged
1349                }
1350                else if(l2 != (l2 = item[i].length))
1351                {
1352                    regular = 'J';  // J for jagged
1353                    sum += l2;
1354                }
1355            }
1356            StringBuilder sb;
1357            if(regular == 'R')
1358            {
1359                sb = new StringBuilder(len * l2 + 15);
1360                sb.append('R').append(len).append('|').append(l2).append('|');
1361                for (int i = 0; i < len; i++) {
1362                    sb.append(item[i]);
1363                }
1364            }
1365            else
1366            {
1367                sb = new StringBuilder(len * 7 + sum + 8);
1368                sb.append('J').append(len).append('|');
1369                for (int i = 0; i < len; i++) {
1370                    if(item[i] == null)
1371                        sb.append("-|");
1372                    else
1373                        sb.append(item[i].length).append('|').append(item[i]);
1374                }
1375            }
1376            return sb.toString();
1377        }
1378
1379        @Override
1380        public char[][] restore(String text) {
1381            if(text == null || text.length() <= 1) return null;
1382            if(text.charAt(0) == 'R')
1383            {
1384                int width, height, start = 1, end = text.indexOf('|');
1385                width = StringKit.intFromDec(text, start, end);
1386                start = end+1;
1387                end = text.indexOf('|', start);
1388                height = StringKit.intFromDec(text, start, end);
1389                start = end+1;
1390                char[][] val = new char[width][height];
1391                for (int i = 0; i < width; i++) {
1392                    text.getChars(start, start += height, val[i], 0);
1393                }
1394                return val;
1395            }
1396            else
1397            {
1398                int width, current, start = 1, end = text.indexOf('|');
1399                width = StringKit.intFromDec(text, start, end);
1400                start = end + 1;
1401                char[][] val = new char[width][];
1402                for (int i = 0; i < width; i++) {
1403                    end = text.indexOf('|', start);
1404                    if (text.charAt(start) == '-') {
1405                        val[i] = null;
1406                        start = end + 1;
1407                    } else {
1408                        current = StringKit.intFromDec(text, start, end);
1409                        start = end + 1;
1410                        val[i] = new char[current];
1411                        text.getChars(start, start += current, val[i], 0);
1412                    }
1413                }
1414                return val;
1415            }
1416        }
1417    };
1418
1419}