Home | History | Annotate | Download | only in writer
      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.Optional;
     20 import com.google.common.collect.FluentIterable;
     21 import com.google.common.collect.ImmutableList;
     22 import com.google.common.collect.Iterables;
     23 import com.google.common.collect.Lists;
     24 import com.google.common.collect.Sets;
     25 import java.io.IOException;
     26 import java.util.List;
     27 import java.util.Set;
     28 import javax.lang.model.element.Modifier;
     29 import javax.lang.model.element.TypeElement;
     30 
     31 import static com.google.common.base.Preconditions.checkState;
     32 import static javax.lang.model.element.Modifier.PRIVATE;
     33 import static javax.lang.model.element.Modifier.PROTECTED;
     34 import static javax.lang.model.element.Modifier.PUBLIC;
     35 
     36 public final class ClassWriter extends TypeWriter {
     37   private Optional<TypeName> superclass;
     38   private final List<ConstructorWriter> constructorWriters;
     39   private final List<TypeVariableName> typeParameters;
     40 
     41   ClassWriter(ClassName className) {
     42     super(className);
     43     this.superclass = Optional.absent();
     44     this.constructorWriters = Lists.newArrayList();
     45     this.typeParameters = Lists.newArrayList();
     46   }
     47 
     48   public void setSuperclass(TypeName typeReference) {
     49     checkState(!superclass.isPresent());
     50     superclass = Optional.of(typeReference);
     51   }
     52 
     53   /**
     54    * If {@code supertype} is a class, makes this class extend it; if it is an interface, makes this
     55    * class implement it.
     56    */
     57   public void setSupertype(TypeElement supertype) {
     58     switch (supertype.getKind()) {
     59       case CLASS:
     60         setSuperclass(ClassName.fromTypeElement(supertype));
     61         break;
     62       case INTERFACE:
     63         addImplementedType(supertype);
     64         break;
     65       default:
     66         throw new IllegalArgumentException(supertype + " must be a class or interface");
     67     }
     68   }
     69 
     70   public ConstructorWriter addConstructor() {
     71     ConstructorWriter constructorWriter = new ConstructorWriter(name.simpleName());
     72     constructorWriters.add(constructorWriter);
     73     return constructorWriter;
     74   }
     75 
     76   public void addTypeParameter(TypeVariableName typeVariableName) {
     77     this.typeParameters.add(typeVariableName);
     78   }
     79 
     80   public void addTypeParameters(Iterable<TypeVariableName> typeVariableNames) {
     81     Iterables.addAll(typeParameters, typeVariableNames);
     82   }
     83 
     84   public List<TypeVariableName> typeParameters() {
     85     return ImmutableList.copyOf(typeParameters);
     86   }
     87 
     88   @Override
     89   public Appendable write(Appendable appendable, Context context) throws IOException {
     90     context = context.createSubcontext(FluentIterable.from(nestedTypeWriters)
     91         .transform(new Function<TypeWriter, ClassName>() {
     92           @Override public ClassName apply(TypeWriter input) {
     93             return input.name;
     94           }
     95         })
     96         .toSet());
     97     writeAnnotations(appendable, context);
     98     writeModifiers(appendable).append("class ").append(name.simpleName());
     99     Writables.join(", ", typeParameters, "<", ">", appendable, context);
    100     if (superclass.isPresent()) {
    101       appendable.append(" extends ");
    102       superclass.get().write(appendable, context);
    103     }
    104     Writables.join(", ", implementedTypes, " implements ", "", appendable, context);
    105     appendable.append(" {");
    106     if (!fieldWriters.isEmpty()) {
    107       appendable.append('\n');
    108     }
    109     for (VariableWriter fieldWriter : fieldWriters.values()) {
    110       fieldWriter.write(new IndentingAppendable(appendable), context).append("\n");
    111     }
    112     for (ConstructorWriter constructorWriter : constructorWriters) {
    113       appendable.append('\n');
    114       if (!isDefaultConstructor(constructorWriter)) {
    115         constructorWriter.write(new IndentingAppendable(appendable), context);
    116       }
    117     }
    118     for (MethodWriter methodWriter : methodWriters) {
    119       appendable.append('\n');
    120       methodWriter.write(new IndentingAppendable(appendable), context);
    121     }
    122     for (TypeWriter nestedTypeWriter : nestedTypeWriters) {
    123       appendable.append('\n');
    124       nestedTypeWriter.write(new IndentingAppendable(appendable), context);
    125     }
    126     appendable.append("}\n");
    127     return appendable;
    128   }
    129 
    130   private static final Set<Modifier> VISIBILIY_MODIFIERS =
    131       Sets.immutableEnumSet(PUBLIC, PROTECTED, PRIVATE);
    132 
    133   private boolean isDefaultConstructor(ConstructorWriter constructorWriter) {
    134     return Sets.intersection(VISIBILIY_MODIFIERS, modifiers)
    135         .equals(Sets.intersection(VISIBILIY_MODIFIERS, constructorWriter.modifiers))
    136         && constructorWriter.body().isEmpty();
    137   }
    138 
    139   @Override
    140   public Set<ClassName> referencedClasses() {
    141     return FluentIterable.from(ImmutableList.<HasClassReferences>of())
    142         .append(nestedTypeWriters)
    143         .append(fieldWriters.values())
    144         .append(constructorWriters)
    145         .append(methodWriters)
    146         .append(implementedTypes)
    147         .append(superclass.asSet())
    148         .append(annotations)
    149         .append(typeParameters)
    150         .transformAndConcat(HasClassReferences.COMBINER)
    151         .toSet();
    152   }
    153 }
    154