15d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin/*
25d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * Copyright (C) 2014 Google, Inc.
35d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin *
45d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * Licensed under the Apache License, Version 2.0 (the "License");
55d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * you may not use this file except in compliance with the License.
65d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * You may obtain a copy of the License at
75d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin *
85d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * http://www.apache.org/licenses/LICENSE-2.0
95d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin *
105d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * Unless required by applicable law or agreed to in writing, software
115d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * distributed under the License is distributed on an "AS IS" BASIS,
125d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * See the License for the specific language governing permissions and
145d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * limitations under the License.
155d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin */
165d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinpackage dagger.internal.codegen.writer;
175d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
185d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport com.google.common.base.Function;
195d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport com.google.common.base.Optional;
205d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport com.google.common.collect.FluentIterable;
215d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport com.google.common.collect.ImmutableList;
225d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport com.google.common.collect.Iterables;
235d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport com.google.common.collect.Lists;
245d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport com.google.common.collect.Sets;
255d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport java.io.IOException;
265d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport java.util.List;
275d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport java.util.Set;
285d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport javax.lang.model.element.Modifier;
295d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport javax.lang.model.element.TypeElement;
305d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
315d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static com.google.common.base.Preconditions.checkState;
325d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static javax.lang.model.element.Modifier.PRIVATE;
335d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static javax.lang.model.element.Modifier.PROTECTED;
345d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static javax.lang.model.element.Modifier.PUBLIC;
355d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
365d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinpublic final class ClassWriter extends TypeWriter {
375d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  private Optional<TypeName> superclass;
385d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  private final List<ConstructorWriter> constructorWriters;
395d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  private final List<TypeVariableName> typeParameters;
405d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
415d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  ClassWriter(ClassName className) {
425d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    super(className);
435d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    this.superclass = Optional.absent();
445d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    this.constructorWriters = Lists.newArrayList();
455d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    this.typeParameters = Lists.newArrayList();
465d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
475d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
485d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  public void setSuperclass(TypeName typeReference) {
495d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    checkState(!superclass.isPresent());
505d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    superclass = Optional.of(typeReference);
515d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
525d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
535d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  /**
545d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin   * If {@code supertype} is a class, makes this class extend it; if it is an interface, makes this
555d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin   * class implement it.
565d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin   */
575d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  public void setSupertype(TypeElement supertype) {
585d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    switch (supertype.getKind()) {
595d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      case CLASS:
605d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        setSuperclass(ClassName.fromTypeElement(supertype));
615d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        break;
625d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      case INTERFACE:
635d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        addImplementedType(supertype);
645d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        break;
655d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      default:
665d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        throw new IllegalArgumentException(supertype + " must be a class or interface");
675d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
685d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
695d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
705d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  public ConstructorWriter addConstructor() {
715d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    ConstructorWriter constructorWriter = new ConstructorWriter(name.simpleName());
725d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    constructorWriters.add(constructorWriter);
735d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    return constructorWriter;
745d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
755d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
765d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  public void addTypeParameter(TypeVariableName typeVariableName) {
775d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    this.typeParameters.add(typeVariableName);
785d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
795d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
805d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  public void addTypeParameters(Iterable<TypeVariableName> typeVariableNames) {
815d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    Iterables.addAll(typeParameters, typeVariableNames);
825d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
835d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
845d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  public List<TypeVariableName> typeParameters() {
855d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    return ImmutableList.copyOf(typeParameters);
865d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
875d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
885d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  @Override
895d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  public Appendable write(Appendable appendable, Context context) throws IOException {
905d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    context = context.createSubcontext(FluentIterable.from(nestedTypeWriters)
915d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        .transform(new Function<TypeWriter, ClassName>() {
925d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          @Override public ClassName apply(TypeWriter input) {
935d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin            return input.name;
945d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          }
955d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        })
965d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        .toSet());
975d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    writeAnnotations(appendable, context);
985d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    writeModifiers(appendable).append("class ").append(name.simpleName());
995d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    Writables.join(", ", typeParameters, "<", ">", appendable, context);
1005d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    if (superclass.isPresent()) {
1015d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      appendable.append(" extends ");
1025d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      superclass.get().write(appendable, context);
1035d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
1045d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    Writables.join(", ", implementedTypes, " implements ", "", appendable, context);
1055d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    appendable.append(" {");
1065d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    if (!fieldWriters.isEmpty()) {
1075d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      appendable.append('\n');
1085d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
1095d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    for (VariableWriter fieldWriter : fieldWriters.values()) {
1105d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      fieldWriter.write(new IndentingAppendable(appendable), context).append("\n");
1115d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
1125d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    for (ConstructorWriter constructorWriter : constructorWriters) {
1135d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      appendable.append('\n');
1145d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      if (!isDefaultConstructor(constructorWriter)) {
1155d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        constructorWriter.write(new IndentingAppendable(appendable), context);
1165d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      }
1175d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
1185d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    for (MethodWriter methodWriter : methodWriters) {
1195d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      appendable.append('\n');
1205d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      methodWriter.write(new IndentingAppendable(appendable), context);
1215d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
1225d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    for (TypeWriter nestedTypeWriter : nestedTypeWriters) {
1235d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      appendable.append('\n');
1245d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      nestedTypeWriter.write(new IndentingAppendable(appendable), context);
1255d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
1265d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    appendable.append("}\n");
1275d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    return appendable;
1285d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
1295d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
1305d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  private static final Set<Modifier> VISIBILIY_MODIFIERS =
1315d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      Sets.immutableEnumSet(PUBLIC, PROTECTED, PRIVATE);
1325d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
1335d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  private boolean isDefaultConstructor(ConstructorWriter constructorWriter) {
1345d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    return Sets.intersection(VISIBILIY_MODIFIERS, modifiers)
1355d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        .equals(Sets.intersection(VISIBILIY_MODIFIERS, constructorWriter.modifiers))
1365d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        && constructorWriter.body().isEmpty();
1375d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
1385d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
1395d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  @Override
1405d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  public Set<ClassName> referencedClasses() {
1415d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    return FluentIterable.from(ImmutableList.<HasClassReferences>of())
1425d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        .append(nestedTypeWriters)
1435d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        .append(fieldWriters.values())
1445d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        .append(constructorWriters)
1455d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        .append(methodWriters)
1465d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        .append(implementedTypes)
1475d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        .append(superclass.asSet())
1485d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        .append(annotations)
1495d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        .append(typeParameters)
1505d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        .transformAndConcat(HasClassReferences.COMBINER)
1515d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        .toSet();
1525d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
1535d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin}
154