KCode.kt revision 39113ca579a3d4e1c24e204f102e6dc9b26125af
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *      http://www.apache.org/licenses/LICENSE-2.0
7 * Unless required by applicable law or agreed to in writing, software
8 * distributed under the License is distributed on an "AS IS" BASIS,
9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 * See the License for the specific language governing permissions and
11 * limitations under the License.
12 */
13
14package android.databinding.tool.writer
15
16import android.databinding.tool.util.StringUtils
17import java.util.BitSet
18
19class KCode (private val s : String? = null){
20
21    private var sameLine = false
22
23    private val lineSeparator = StringUtils.LINE_SEPARATOR
24
25    class Appendix(val glue : String, val code : KCode)
26
27    private val nodes = arrayListOf<Any>()
28
29    companion object {
30        private val cachedIndentations = BitSet()
31        private val indentCache = arrayListOf<String>()
32        fun indent(n: Int): String {
33            if (cachedIndentations.get(n)) {
34                return indentCache.get(n)
35            }
36            val s = (0..n-1).fold(""){prev, next -> "${prev}    "}
37            cachedIndentations.set(n, true )
38            while (indentCache.size <= n) {
39                indentCache.add("");
40            }
41            indentCache.set(n, s)
42            return s
43        }
44    }
45
46    fun isNull(kcode : KCode?) = kcode == null || (kcode.nodes.isEmpty() && (kcode.s == null || kcode.s.trim() == ""))
47
48    fun tab(vararg codes : KCode?) : KCode {
49        codes.forEach { tab(it) }
50        return this
51    }
52
53    fun tab(codes : Collection<KCode?> ) : KCode {
54        codes.forEach { tab(it) }
55        return this
56    }
57
58    infix fun tab(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
59        val c = KCode(s)
60        if (init != null) {
61            c.init()
62        }
63        return tab(c)
64    }
65
66    fun tab(c : KCode?) : KCode {
67        if (c == null || isNull(c)) {
68            return this
69        }
70        nodes.add(c)
71        return this
72    }
73
74    fun nls(vararg codes : KCode?) : KCode {
75        codes.forEach { nl(it) }
76        return this
77    }
78
79    fun nls(codes : Collection<KCode?>) : KCode {
80        codes.forEach { nl(it) }
81        return this
82    }
83
84    infix fun nl(c : KCode?) : KCode {
85        if (c == null || isNull(c)) {
86            return this
87        }
88        nodes.add(c)
89        c!!.sameLine = true
90        return this
91    }
92
93    infix fun nl(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
94        val c = KCode(s)
95        if (init != null) {
96            c.init()
97        }
98        return nl(c)
99    }
100
101    fun apps(glue : String = "", vararg codes : KCode?) : KCode {
102        codes.forEach { app(glue, it)}
103        return this
104    }
105
106    fun apps(glue : String = "", codes : Collection<KCode?>) : KCode {
107        codes.forEach { app(glue, it)}
108        return this
109    }
110
111    fun app(glue : String = "", c : KCode?) : KCode {
112        if (isNull(c)) {
113            return this
114        }
115        nodes.add(Appendix(glue, c!!))
116        return this
117    }
118
119    infix fun app(s : String) : KCode {
120        val c = KCode(s)
121        return app("", c)
122    }
123
124    fun app(glue : String = "", s : String?, init : (KCode.() -> Unit)? = null) : KCode {
125        val c = KCode(s)
126        if (init != null) {
127            c.init()
128        }
129        return app(glue, c)
130    }
131
132
133    fun toS(n : Int, sb : StringBuilder) {
134        if (s != null) {
135            sb.append(s)
136        }
137        val newlineFirstNode = s != null || (nodes.isNotEmpty() && nodes.first() is Appendix)
138        var addedChild = false
139        nodes.forEach { when(it) {
140            is Appendix -> {
141                sb.append(it.glue)
142                it.code.toS(n, sb)
143            }
144            is KCode -> {
145                val childTab = n + (if(it.sameLine) 0 else 1)
146                if (addedChild || newlineFirstNode) {
147                    sb.append(lineSeparator)
148                    sb.append("${indent(childTab)}")
149                }
150                it.toS(childTab, sb)
151                addedChild = true
152            }
153        } }
154
155    }
156
157    fun generate() : String {
158        val sb = StringBuilder()
159        toS(0, sb)
160        return sb.toString()
161    }
162}
163
164fun kcode(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
165    val c = KCode(s)
166    if (init != null) {
167        c.init()
168    }
169    return c
170}
171