001/*
002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others
003 *
004 * This program and the accompanying materials are made available under the
005 * terms of the Eclipse Public License 2.0 which is available at
006 * http://www.eclipse.org/legal/epl-2.0.
007 *
008 * SPDX-License-Identifier: EPL-2.0
009 */
010
011package gololang.ir;
012
013import java.util.*;
014
015import static gololang.ir.GoloFunction.function;
016import static java.util.stream.Collectors.toList;
017
018/**
019 * An abstract class for struct-like types.
020 */
021public abstract class TypeWithMembers<T extends TypeWithMembers<T>> extends GoloType<T> {
022  private final Set<Member> members = new LinkedHashSet<>();
023
024  TypeWithMembers(String name) {
025    super(name);
026  }
027
028  protected String getFactoryDelegateName() {
029    return getFullName();
030  }
031
032  String getFullName() {
033    return getPackageAndClass().toString();
034  }
035
036  /**
037   * Adds all members to this type.
038   *
039   * @see #withMember(Object)
040   */
041  public T members(Object... members) {
042    for (Object member : members) {
043      withMember(member);
044    }
045    return self();
046  }
047
048  public boolean hasMembers() {
049    return !this.members.isEmpty();
050  }
051
052  protected void addMember(Member member) {
053    this.members.add(makeParentOf(member));
054  }
055
056  void addMembers(Iterable<Member> members) {
057    members.forEach(this::addMember);
058  }
059
060  /**
061   * Adds a member to this type.
062   *
063   * <p>This is a builder method.
064   *
065   * @param member a {@link Member} or any object whose {@code toString} method is used to create a member object.
066   */
067  public T withMember(Object member) {
068    addMember(Member.of(member));
069    return self();
070  }
071
072  protected List<String> getMemberNames() {
073    return members.stream()
074      .map(Member::getName)
075      .collect(toList());
076  }
077
078  public Set<Member> getMembers() {
079    return Collections.unmodifiableSet(members);
080  }
081
082  public List<Member> getPublicMembers() {
083    return members.stream()
084      .filter(Member::isPublic)
085      .collect(toList());
086  }
087
088  protected Object[] getFullArgs() {
089    return members.stream()
090      .map(Member::getName)
091      .map(ReferenceLookup::of)
092      .toArray();
093  }
094
095  /**
096   * Creates the factory functions for this type.
097   *
098   * <p>Internal API
099   */
100  public Set<GoloFunction> createFactories() {
101    Set<GoloFunction> factories = new LinkedHashSet<>();
102    factories.add(createFullArgsConstructor());
103    return factories;
104  }
105
106  protected GoloFunction createFullArgsConstructor() {
107    return function(getName()).synthetic()
108      .withParameters(getMemberNames())
109      .returns(FunctionInvocation.of(getFactoryDelegateName()).withArgs(getFullArgs()));
110  }
111
112  /**
113   * {@inheritDoc}
114   */
115  @Override
116  public List<GoloElement<?>> children() {
117    return new ArrayList<>(members);
118  }
119
120  /**
121   * {@inheritDoc}
122   */
123  @Override
124  protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) {
125    throw cantReplace();
126  }
127}