001/*
002 * PermissionsEx
003 * Copyright (C) zml and PermissionsEx contributors
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package ca.stellardrift.permissionsex.datastore.sql;
018
019import ca.stellardrift.permissionsex.datastore.DataStoreContext;
020import ca.stellardrift.permissionsex.subject.SubjectRef;
021import ca.stellardrift.permissionsex.subject.SubjectType;
022import org.checkerframework.checker.nullness.qual.Nullable;
023
024import java.util.Objects;
025
026public class SqlSubjectRef<I> implements SubjectRef<I> {
027    private volatile int id;
028    private @Nullable volatile SubjectRef<I> resolved = null;
029    private final DataStoreContext context;
030    private final String type, identifier;
031
032    SqlSubjectRef(final DataStoreContext context, final int id, final String type, final String identifier) {
033        this.context = context;
034        this.id = id;
035        this.type = type;
036        this.identifier = identifier;
037    }
038
039    SqlSubjectRef(final SubjectRef<I> existing) {
040        this.id = SqlConstants.UNALLOCATED;
041        this.context = null;
042        this.type = existing.type().name();
043        this.identifier = null;
044        this.resolved = SubjectRef.mapKeySafe(existing);
045    }
046
047    public static <I> SqlSubjectRef<I> unresolved(final DataStoreContext context, final String type, final String name) {
048        return new SqlSubjectRef<>(context, SqlConstants.UNALLOCATED, type, name);
049    }
050
051    public int id() {
052        if (id == SqlConstants.UNALLOCATED) {
053            throw new IllegalStateException("Unallocated SubjectRef tried to be used!");
054        }
055        return id;
056    }
057
058    public void id(int id) {
059        this.id = id;
060    }
061
062    boolean isUnallocated() {
063        return id == SqlConstants.UNALLOCATED;
064    }
065
066    public String rawType() {
067        return type;
068    }
069
070    public String rawIdentifier() {
071        if (this.identifier == null) {
072            final @Nullable SubjectRef<I> resolved = this.resolved;
073            if (resolved == null) {
074                throw new IllegalStateException("Unable to get an identifier for this subject ref");
075            }
076            return resolved.serializedIdentifier();
077        }
078        return this.identifier;
079    }
080
081    @Override
082    public String serializedIdentifier() {
083        final @Nullable SubjectRef<?> resolved = this.resolved;
084        if (resolved != null) {
085            return resolved.serializedIdentifier();
086        } else {
087            return this.identifier;
088        }
089    }
090
091    @Override
092    public SubjectType<I> type() {
093        return this.resolved().type();
094    }
095
096    @Override
097    public I identifier() {
098        return this.resolved().identifier();
099    }
100
101    @SuppressWarnings({"unchecked"})
102    public SubjectRef<I> resolved() {
103        if (this.resolved != null) {
104            return this.resolved;
105        }
106        return this.resolved = (SubjectRef<I>) this.context.deserializeSubjectRef(this.type, this.identifier);
107    }
108
109    @Override
110    public boolean equals(final Object other) {
111        if (this == other) return true;
112        if (!(other instanceof SqlSubjectRef)) return false;
113        final SqlSubjectRef<?> that = (SqlSubjectRef<?>) other;
114        return Objects.equals(type, that.type) &&
115                Objects.equals(identifier, that.identifier);
116    }
117
118    @Override
119    public int hashCode() {
120        return Objects.hash(this.type, this.identifier);
121    }
122
123    @Override
124    public String toString() {
125        return "SubjectRef{id=" + (this.id == SqlConstants.UNALLOCATED ? "<unallocated>" : this.id)
126                + ",type=" + this.type
127                + ",identifier=" + this.identifier
128                + "}";
129    }
130
131    public static <I> SqlSubjectRef<I> from(final SubjectRef<I> existing) {
132        if (existing instanceof SqlSubjectRef<?>) {
133            return (SqlSubjectRef<I>) existing;
134        } else {
135            return new SqlSubjectRef<>(existing);
136        }
137    }
138}