1 /* 2 * Copyright (C) 2014 Google, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package dagger.internal.codegen.writer; 17 18 import com.google.common.base.Function; 19 import com.google.common.base.Joiner; 20 import com.google.common.collect.FluentIterable; 21 import com.google.common.collect.ImmutableList; 22 import com.google.common.collect.ImmutableSet; 23 import com.google.common.collect.Iterables; 24 import java.io.IOException; 25 import java.util.Collections; 26 import java.util.Formatter; 27 import java.util.Iterator; 28 import java.util.Set; 29 30 public abstract class Snippet implements HasClassReferences, Writable { 31 types()32 abstract ImmutableSet<TypeName> types(); 33 34 @Override toString()35 public String toString() { 36 return Writables.writeToString(this); 37 } 38 39 @Override referencedClasses()40 public final Set<ClassName> referencedClasses() { 41 return FluentIterable.from(types()) 42 .transformAndConcat( 43 new Function<TypeName, Set<ClassName>>() { 44 @Override 45 public Set<ClassName> apply(TypeName input) { 46 return input.referencedClasses(); 47 } 48 }) 49 .toSet(); 50 } 51 52 private static final class BasicSnippet extends Snippet { 53 final String format; 54 final ImmutableSet<TypeName> types; 55 final ImmutableList<Object> args; 56 57 BasicSnippet(String format, ImmutableSet<TypeName> types, ImmutableList<Object> args) { 58 this.format = format; 59 this.types = types; 60 this.args = args; 61 } 62 63 @Override 64 ImmutableSet<TypeName> types() { 65 return types; 66 } 67 68 @Override 69 public Appendable write(Appendable appendable, Context context) throws IOException { 70 ImmutableList.Builder<Object> formattedArgsBuilder = ImmutableList.builder(); 71 for (Object arg : args) { 72 if (arg instanceof Writable) { 73 formattedArgsBuilder.add(((Writable) arg).write(new StringBuilder(), context).toString()); 74 } else { 75 formattedArgsBuilder.add(arg); 76 } 77 } 78 79 @SuppressWarnings("resource") // intentionally don't close the formatter 80 Formatter formatter = new Formatter(appendable); 81 formatter.format(format, Iterables.toArray(formattedArgsBuilder.build(), Object.class)); 82 83 return appendable; 84 } 85 } 86 87 private static final class CompoundSnippet extends Snippet { 88 final String joinToken; 89 final ImmutableList<Snippet> snippets; 90 91 CompoundSnippet(String joinToken, ImmutableList<Snippet> snippets) { 92 this.joinToken = joinToken; 93 this.snippets = snippets; 94 } 95 96 @Override 97 ImmutableSet<TypeName> types() { 98 return FluentIterable.from(snippets) 99 .transformAndConcat( 100 new Function<Snippet, Iterable<TypeName>>() { 101 @Override 102 public Iterable<TypeName> apply(Snippet input) { 103 return input.types(); 104 } 105 }) 106 .toSet(); 107 } 108 109 @Override 110 public Appendable write(Appendable appendable, Context context) throws IOException { 111 Iterator<Snippet> snippetIterator = snippets.iterator(); 112 if (snippetIterator.hasNext()) { 113 Snippet firstSnippet = snippetIterator.next(); 114 firstSnippet.write(appendable, context); 115 while (snippetIterator.hasNext()) { 116 Snippet nextSnippet = snippetIterator.next(); 117 appendable.append(joinToken); 118 nextSnippet.write(appendable, context); 119 } 120 } 121 return appendable; 122 } 123 } 124 125 public static Snippet format(String format, Object... args) { 126 ImmutableSet.Builder<TypeName> types = ImmutableSet.builder(); 127 for (Object arg : args) { 128 if (arg instanceof Snippet) { 129 types.addAll(((Snippet) arg).types()); 130 } 131 if (arg instanceof TypeName) { 132 types.add((TypeName) arg); 133 } 134 if (arg instanceof HasTypeName) { 135 types.add(((HasTypeName) arg).name()); 136 } 137 } 138 return new BasicSnippet(format, types.build(), ImmutableList.copyOf(args)); 139 } 140 141 public static Snippet format(String format, Iterable<? extends Object> args) { 142 return format(format, Iterables.toArray(args, Object.class)); 143 } 144 145 public static Snippet memberSelectSnippet(Iterable<? extends Object> selectors) { 146 return format(Joiner.on('.').join(Collections.nCopies(Iterables.size(selectors), "%s")), 147 selectors); 148 } 149 150 public static Snippet nullCheck(Object thingToCheck) { 151 return format("if (%s == null) { throw new NullPointerException(); } ", thingToCheck); 152 } 153 154 public static Snippet nullCheck(Object thingToCheck, String message) { 155 return format("if (%s == null) { throw new NullPointerException(%s); } ", 156 thingToCheck, 157 StringLiteral.forValue(message)); 158 } 159 160 public static Snippet makeParametersSnippet(Iterable<Snippet> parameterSnippets) { 161 return join(", ", parameterSnippets); 162 } 163 164 /** 165 * A snippet that concatenates its arguments with each snippet separated by a new line. 166 */ 167 public static Snippet concat(Iterable<Snippet> snippets) { 168 return join("\n", snippets); 169 } 170 171 /** 172 * A snippet that joins its arguments with {@code joiner}. 173 */ 174 public static Snippet join(String joinToken, Iterable<Snippet> snippets) { 175 return new CompoundSnippet(joinToken, ImmutableList.copyOf(snippets)); 176 } 177 } 178