001/**
002 *
003 * Copyright 2014-2019 Florian Schmaus
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smack.util;
018
019import java.util.ArrayList;
020import java.util.Collections;
021import java.util.List;
022
023public class LazyStringBuilder implements Appendable, CharSequence {
024
025    private final List<CharSequence> list;
026
027    private String cache;
028
029    private void invalidateCache() {
030        cache = null;
031    }
032
033    public LazyStringBuilder() {
034        list = new ArrayList<>(20);
035    }
036
037    public LazyStringBuilder append(LazyStringBuilder lsb) {
038        list.addAll(lsb.list);
039        invalidateCache();
040        return this;
041    }
042
043    @Override
044    public LazyStringBuilder append(CharSequence csq) {
045        assert csq != null;
046        list.add(csq);
047        invalidateCache();
048        return this;
049    }
050
051    @Override
052    public LazyStringBuilder append(CharSequence csq, int start, int end) {
053        CharSequence subsequence = csq.subSequence(start, end);
054        list.add(subsequence);
055        invalidateCache();
056        return this;
057    }
058
059    @Override
060    public LazyStringBuilder append(char c) {
061        list.add(Character.toString(c));
062        invalidateCache();
063        return this;
064    }
065
066    @Override
067    public int length() {
068        if (cache != null) {
069            return cache.length();
070        }
071        int length = 0;
072        try {
073            for (CharSequence csq : list) {
074                length += csq.length();
075            }
076        }
077        catch (NullPointerException npe) {
078            StringBuilder sb = safeToStringBuilder();
079            throw new RuntimeException("The following LazyStringBuilder threw a NullPointerException:  " + sb, npe);
080        }
081        return length;
082    }
083
084    @Override
085    public char charAt(int index) {
086        if (cache != null) {
087            return cache.charAt(index);
088        }
089        for (CharSequence csq : list) {
090            if (index < csq.length()) {
091                return csq.charAt(index);
092            } else {
093                index -= csq.length();
094            }
095        }
096        throw new IndexOutOfBoundsException();
097    }
098
099    @Override
100    public CharSequence subSequence(int start, int end) {
101        return toString().subSequence(start, end);
102    }
103
104    @Override
105    public String toString() {
106        if (cache == null) {
107            StringBuilder sb = new StringBuilder(length());
108            for (CharSequence csq : list) {
109                sb.append(csq);
110            }
111            cache = sb.toString();
112        }
113        return cache;
114    }
115
116    public StringBuilder safeToStringBuilder() {
117        StringBuilder sb = new StringBuilder();
118        for (CharSequence csq : list) {
119            sb.append(csq);
120        }
121        return sb;
122    }
123
124    /**
125     * Get the List of CharSequences representation of this instance. The list is unmodifiable. If
126     * the resulting String was already cached, a list with a single String entry will be returned.
127     *
128     * @return a List of CharSequences representing this instance.
129     */
130    public List<CharSequence> getAsList() {
131        if (cache != null) {
132            return Collections.singletonList((CharSequence) cache);
133        }
134        return Collections.unmodifiableList(list);
135    }
136}