public abstract class AbstractProjectNativeComponent implements ProjectNativeComponentInternal {
  private final NotationParser<Object, Set<LanguageSourceSet>> sourcesNotationParser =
      SourceSetNotationParser.parser();
  private final NativeProjectComponentIdentifier id;
  private final DomainObjectSet<LanguageSourceSet> sourceSets;
  private final DefaultDomainObjectSet<NativeBinary> binaries;
  private final Set<String> targetPlatforms = new HashSet<String>();
  private final Set<String> buildTypes = new HashSet<String>();
  private final Set<String> flavors = new HashSet<String>();
  private String baseName;

  public AbstractProjectNativeComponent(NativeProjectComponentIdentifier id) {
    this.id = id;
    this.sourceSets = new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet.class);
    binaries = new DefaultDomainObjectSet<NativeBinary>(NativeBinary.class);
  }

  @Override
  public String toString() {
    return getDisplayName();
  }

  public String getProjectPath() {
    return id.getProjectPath();
  }

  public String getName() {
    return id.getName();
  }

  public DomainObjectSet<LanguageSourceSet> getSource() {
    return sourceSets;
  }

  public void source(Object sources) {
    sourceSets.addAll(sourcesNotationParser.parseNotation(sources));
  }

  public DomainObjectSet<NativeBinary> getBinaries() {
    return binaries;
  }

  public String getBaseName() {
    return GUtil.elvis(baseName, getName());
  }

  public void setBaseName(String baseName) {
    this.baseName = baseName;
  }

  public void targetFlavors(Object... flavorSelectors) {
    for (Object flavorSelector : flavorSelectors) {
      // TODO:DAZ Allow Flavor instance and selector
      assert flavorSelector instanceof String;
      flavors.add((String) flavorSelector);
    }
  }

  public void targetPlatforms(Object... platformSelectors) {
    for (Object platformSelector : platformSelectors) {
      // TODO:DAZ Allow Platform instance and selector
      assert platformSelector instanceof String;
      targetPlatforms.add((String) platformSelector);
    }
  }

  public void targetBuildTypes(Object... buildTypeSelectors) {
    for (Object buildTypeSelector : buildTypeSelectors) {
      // TODO:DAZ Allow BuildType instance and selector
      assert buildTypeSelector instanceof String;
      buildTypes.add((String) buildTypeSelector);
    }
  }

  public Set<Flavor> chooseFlavors(Set<? extends Flavor> candidates) {
    return chooseElements(Flavor.class, candidates, flavors);
  }

  public Set<BuildType> chooseBuildTypes(Set<? extends BuildType> candidates) {
    return chooseElements(BuildType.class, candidates, buildTypes);
  }

  public Set<Platform> choosePlatforms(Set<? extends Platform> candidates) {
    return chooseElements(Platform.class, candidates, targetPlatforms);
  }

  private <T extends Named> Set<T> chooseElements(
      Class<T> type, Set<? extends T> candidates, final Set<String> names) {
    if (names.isEmpty()) {
      return new LinkedHashSet<T>(candidates);
    }

    Set<String> unusedNames = new HashSet<String>(names);
    Set<T> chosen = new LinkedHashSet<T>();
    for (T candidate : candidates) {
      if (unusedNames.remove(candidate.getName())) {
        chosen.add(candidate);
      }
    }

    if (!unusedNames.isEmpty()) {
      throw new InvalidUserDataException(
          String.format("Invalid %s: '%s'", type.getSimpleName(), unusedNames.iterator().next()));
    }

    return chosen;
  }
}
public abstract class DefaultNativeBinary extends AbstractBuildableModelElement
    implements NativeBinaryInternal {
  private final NotationParser<Set<LanguageSourceSet>> sourcesNotationParser =
      SourceSetNotationParser.parser();
  private final ResolvableNativeDependencySet libs = new ResolvableNativeDependencySet();
  private final DomainObjectSet<LanguageSourceSet> source =
      new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet.class);
  private final ArrayList<Object> compilerArgs = new ArrayList<Object>();
  private final ArrayList<Object> assemblerArgs = new ArrayList<Object>();
  private final ArrayList<Object> linkerArgs = new ArrayList<Object>();
  private final ArrayList<Object> defines = new ArrayList<Object>();
  private final TaskNamerForBinaries namer;
  private final String name;
  private final ToolChainInternal toolChain;
  private BuildBinaryTask builderTask;
  private File outputFile;

  protected DefaultNativeBinary(NativeComponent owner, String name, ToolChainInternal toolChain) {
    this.name = name;
    this.toolChain = toolChain;
    namer = new TaskNamerForBinaries(name);
    owner
        .getSource()
        .all(
            new Action<LanguageSourceSet>() {
              public void execute(LanguageSourceSet sourceSet) {
                source.add(sourceSet);
              }
            });
  }

  public BuildBinaryTask getBuilderTask() {
    return builderTask;
  }

  public void setBuilderTask(BuildBinaryTask builderTask) {
    this.builderTask = builderTask;
    dependsOn(builderTask);
  }

  public String getName() {
    return name;
  }

  public ToolChainInternal getToolChain() {
    return toolChain;
  }

  public File getOutputFile() {
    return outputFile;
  }

  public void setOutputFile(File outputFile) {
    this.outputFile = outputFile;
  }

  public DomainObjectSet<LanguageSourceSet> getSource() {
    return source;
  }

  public void source(Object sources) {
    source.addAll(sourcesNotationParser.parseNotation(sources));
  }

  public List<Object> getMacros() {
    return defines;
  }

  public void define(Object... defines) {
    Collections.addAll(this.defines, defines);
  }

  public List<Object> getCompilerArgs() {
    return compilerArgs;
  }

  public void compilerArgs(Object... args) {
    Collections.addAll(compilerArgs, args);
  }

  public List<Object> getAssemblerArgs() {
    return assemblerArgs;
  }

  public void assemblerArgs(Object... args) {
    Collections.addAll(assemblerArgs, args);
  }

  public List<Object> getLinkerArgs() {
    return linkerArgs;
  }

  public void linkerArgs(Object... args) {
    Collections.addAll(linkerArgs, args);
  }

  public BinaryNamingScheme getNamingScheme() {
    return namer;
  }

  public Collection<NativeDependencySet> getLibs() {
    return libs.resolve();
  }

  public void lib(Object notation) {
    libs.add(notation);
  }

  public abstract String getOutputFileName();

  protected abstract NativeComponent getComponent();
}