LazyStringBuilder.java

  1. /**
  2.  *
  3.  * Copyright 2014-2023 Florian Schmaus
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smack.util;

  18. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.List;

  21. public class LazyStringBuilder implements Appendable, CharSequence {

  22.     private final List<CharSequence> list;

  23.     private transient String cache;
  24.     private int cachedLength = -1;

  25.     private void invalidateCache() {
  26.         cache = null;
  27.         cachedLength = -1;
  28.     }

  29.     public LazyStringBuilder() {
  30.         list = new ArrayList<>(20);
  31.     }

  32.     public LazyStringBuilder append(LazyStringBuilder lsb) {
  33.         list.addAll(lsb.list);
  34.         invalidateCache();
  35.         return this;
  36.     }

  37.     @Override
  38.     public LazyStringBuilder append(CharSequence csq) {
  39.         assert csq != null;
  40.         list.add(csq);
  41.         invalidateCache();
  42.         return this;
  43.     }

  44.     @Override
  45.     public LazyStringBuilder append(CharSequence csq, int start, int end) {
  46.         CharSequence subsequence = csq.subSequence(start, end);
  47.         list.add(subsequence);
  48.         invalidateCache();
  49.         return this;
  50.     }

  51.     @Override
  52.     public LazyStringBuilder append(char c) {
  53.         list.add(Character.toString(c));
  54.         invalidateCache();
  55.         return this;
  56.     }

  57.     @Override
  58.     public int length() {
  59.         if (cachedLength >= 0) {
  60.             return cachedLength;
  61.         }

  62.         int length = 0;
  63.         try {
  64.             for (CharSequence csq : list) {
  65.                 length += csq.length();
  66.             }
  67.         }
  68.         catch (NullPointerException npe) {
  69.             StringBuilder sb = safeToStringBuilder();
  70.             throw new RuntimeException("The following LazyStringBuilder threw a NullPointerException:  " + sb, npe);
  71.         }

  72.         cachedLength = length;
  73.         return length;
  74.     }

  75.     @Override
  76.     public char charAt(int index) {
  77.         if (cache != null) {
  78.             return cache.charAt(index);
  79.         }
  80.         for (CharSequence csq : list) {
  81.             if (index < csq.length()) {
  82.                 return csq.charAt(index);
  83.             } else {
  84.                 index -= csq.length();
  85.             }
  86.         }
  87.         throw new IndexOutOfBoundsException();
  88.     }

  89.     @Override
  90.     public CharSequence subSequence(int start, int end) {
  91.         return toString().subSequence(start, end);
  92.     }

  93.     @Override
  94.     public String toString() {
  95.         if (cache == null) {
  96.             StringBuilder sb = new StringBuilder(length());
  97.             for (CharSequence csq : list) {
  98.                 sb.append(csq);
  99.             }
  100.             cache = sb.toString();
  101.         }
  102.         return cache;
  103.     }

  104.     public StringBuilder safeToStringBuilder() {
  105.         StringBuilder sb = new StringBuilder();
  106.         for (CharSequence csq : list) {
  107.             sb.append(csq);
  108.         }
  109.         return sb;
  110.     }

  111.     /**
  112.      * Get the List of CharSequences representation of this instance. The list is unmodifiable. If
  113.      * the resulting String was already cached, a list with a single String entry will be returned.
  114.      *
  115.      * @return a List of CharSequences representing this instance.
  116.      */
  117.     public List<CharSequence> getAsList() {
  118.         if (cache != null) {
  119.             return Collections.singletonList((CharSequence) cache);
  120.         }
  121.         return Collections.unmodifiableList(list);
  122.     }
  123. }