KCode.kt revision af146d6a8c0efcf5682d14047c06866a5548f78f
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[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[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    infix fun nl(c : KCode?) : KCode {
75        if (c == null || isNull(c)) {
76            return this
77        }
78        nodes.add(c)
79        c.sameLine = true
80        return this
81    }
82
83    infix fun nl(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
84        val c = KCode(s)
85        if (init != null) {
86            c.init()
87        }
88        return nl(c)
89    }
90
91    fun app(glue : String = "", c : KCode?) : KCode {
92        if (isNull(c)) {
93            return this
94        }
95        nodes.add(Appendix(glue, c!!))
96        return this
97    }
98
99    infix fun app(s : String) : KCode {
100        val c = KCode(s)
101        return app("", c)
102    }
103
104    fun app(glue : String = "", s : String?, init : (KCode.() -> Unit)? = null) : KCode {
105        val c = KCode(s)
106        if (init != null) {
107            c.init()
108        }
109        return app(glue, c)
110    }
111
112
113    fun toS(n : Int, sb : StringBuilder) {
114        if (s != null) {
115            sb.append(s)
116        }
117        val newlineFirstNode = s != null || (nodes.isNotEmpty() && nodes.first() is Appendix)
118        var addedChild = false
119        nodes.forEach { when(it) {
120            is Appendix -> {
121                sb.append(it.glue)
122                it.code.toS(n, sb)
123            }
124            is KCode -> {
125                val childTab = n + (if(it.sameLine) 0 else 1)
126                if (addedChild || newlineFirstNode) {
127                    sb.append(lineSeparator)
128                    sb.append("${indent(childTab)}")
129                }
130                it.toS(childTab, sb)
131                addedChild = true
132            }
133        } }
134
135    }
136
137    fun generate() : String {
138        val sb = StringBuilder()
139        toS(0, sb)
140        return sb.toString()
141    }
142}
143
144fun kcode(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
145    val c = KCode(s)
146    if (init != null) {
147        c.init()
148    }
149    return c
150}
151