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