001/*
002 * JDrupes Builder
003 * Copyright (C) 2025, 2026 Michael N. Lipp
004 * 
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.builder.core;
020
021import java.util.Objects;
022import java.util.Optional;
023import org.jdrupes.builder.api.Proxyable;
024import org.jdrupes.builder.api.Resource;
025import org.jdrupes.builder.api.ResourceType;
026
027/// A base class for [Resource]s.
028///
029public abstract class ResourceObject implements Resource, Proxyable {
030
031    private final ResourceType<?> type;
032    private String name;
033    private boolean isLocked;
034
035    /// Create a new instance.
036    ///
037    protected ResourceObject() {
038        this.type = new ResourceType<>(getClass(), null);
039    }
040
041    /// Create a new instance.
042    ///
043    /// @param type the type
044    ///
045    protected ResourceObject(ResourceType<?> type) {
046        this.type = Objects.requireNonNull(type);
047    }
048
049    @Override
050    public ResourceType<?> type() {
051        return type;
052    }
053
054    @Override
055    public Optional<String> name() {
056        return Optional.ofNullable(name);
057    }
058
059    /// Sets the name of the resource.
060    ///
061    /// @param name the name
062    /// @return the resource object
063    ///
064    public ResourceObject name(String name) {
065        if (isLocked) {
066            throw new IllegalStateException(
067                "Name may only be set once immediately after creation.");
068        }
069        isLocked = true;
070        this.name = name;
071        return this;
072    }
073
074    /// Checks if the instance has been used in a comparison (`hashCode`
075    /// or `equals`) and is therefore locked for further modifications.
076    ///
077    /// @return true, if is locked
078    ///
079    public boolean isLocked() {
080        return isLocked;
081    }
082
083    @Override
084    public int hashCode() {
085        isLocked = true;
086        return Objects.hash(type(), name());
087    }
088
089    @Override
090    public boolean equals(Object obj) {
091        isLocked = true;
092        if (this == obj) {
093            return true;
094        }
095        if (obj == null) {
096            return false;
097        }
098        return (obj instanceof ResourceObject other)
099            && Objects.equals(type(), other.type())
100            && Objects.equals(name(), other.name());
101    }
102
103    @Override
104    public String toString() {
105        return type() + name().map(n -> ":" + n).orElse("")
106            + " (" + asOfLocalized() + ")";
107    }
108}