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.impl.rank; 018 019import ca.stellardrift.permissionsex.rank.RankLadder; 020import ca.stellardrift.permissionsex.subject.SubjectRef; 021import ca.stellardrift.permissionsex.context.ContextValue; 022import ca.stellardrift.permissionsex.subject.ImmutableSubjectData; 023import net.kyori.adventure.text.Component; 024import net.kyori.adventure.text.event.ClickEvent; 025import net.kyori.adventure.text.event.HoverEvent; 026import net.kyori.adventure.text.format.TextDecoration; 027import org.pcollections.PVector; 028 029import java.util.ArrayList; 030import java.util.List; 031import java.util.ListIterator; 032import java.util.Set; 033 034public abstract class AbstractRankLadder implements RankLadder { 035 private final String name; 036 037 protected AbstractRankLadder(String name) { 038 this.name = name; 039 } 040 041 @Override 042 public String name() { 043 return this.name; 044 } 045 046 @Override 047 public final ImmutableSubjectData promote(Set<ContextValue<?>> contexts, ImmutableSubjectData input) { 048 if (ranks().isEmpty()) { 049 return input; 050 } 051 052 return input.withSegment(contexts, seg -> { 053 final List<? extends SubjectRef<?>> originalParents = seg.parents(); 054 if (originalParents.isEmpty()) { 055 return seg.plusParent(ranks().get(0)); 056 } else { 057 int index; 058 final List<SubjectRef<?>> parents = new ArrayList<>(originalParents); 059 boolean found = false; 060 for (ListIterator<SubjectRef<?>> it = parents.listIterator(); it.hasNext();) { 061 SubjectRef<?> parent = it.next(); 062 if ((index = ranks().indexOf(parent)) > -1) { 063 if (index == ranks().size() - 1) { 064 return seg; 065 } else { 066 it.set(ranks().get(index + 1)); 067 found = true; 068 } 069 } 070 } 071 072 if (found) { 073 return seg.withParents(parents); // Promotion happened 074 } else { 075 return seg.plusParent(ranks().get(0)); 076 } 077 } 078 }); 079 080 } 081 082 @Override 083 public final ImmutableSubjectData demote(Set<ContextValue<?>> contexts, ImmutableSubjectData input) { 084 if (ranks().isEmpty()) { 085 return input; 086 } 087 088 return input.withSegment(contexts, seg -> { 089 List<? extends SubjectRef<?>> originalParents = seg.parents(); 090 if (originalParents.isEmpty()) { 091 return seg; 092 } else { 093 int index; 094 final List<SubjectRef<?>> parents = new ArrayList<>(originalParents); // TODO: rewrite to work with immutable login 095 boolean found = false; 096 for (ListIterator<SubjectRef<?>> it = parents.listIterator(); it.hasNext();) { 097 SubjectRef<?> parent = it.next(); 098 if ((index = ranks().indexOf(parent)) > -1) { 099 if (index == 0) { 100 // At bottom of rank ladder, remove the rank entirely 101 it.remove(); 102 } else { 103 it.set(ranks().get(index - 1)); 104 } 105 found = true; 106 } 107 } 108 if (found) { 109 return seg.withParents(parents); 110 } else { 111 return seg; 112 } 113 } 114 }); 115 } 116 117 @Override 118 public final boolean isOnLadder(final Set<ContextValue<?>> contexts, final ImmutableSubjectData subject) { 119 if (ranks().isEmpty()) { 120 return false; 121 } 122 123 for (final SubjectRef<?> par : subject.segment(contexts).parents()) { 124 if (ranks().contains(par)) { 125 return true; 126 } 127 } 128 return false; 129 } 130 131 @Override 132 public final int indexOf(final SubjectRef<?> subject) { 133 return ranks().indexOf(subject); 134 } 135 136 @Override 137 public final RankLadder with(final SubjectRef<?> subject) { 138 int indexOf = ranks().indexOf(subject); 139 if (indexOf != -1) { 140 return newWithRanks(this.ranks().minus(indexOf).plus(SubjectRef.mapKeySafe(subject))); 141 } else { 142 return newWithRanks(this.ranks().plus(SubjectRef.mapKeySafe(subject))); 143 } 144 145 } 146 147 148 @Override 149 public final RankLadder with(final SubjectRef<?> subject, int index) { 150 if (index > ranks().size() || index < 0) { 151 return this; 152 } 153 154 PVector<SubjectRef<?>> entries = this.ranks(); 155 int indexOf = entries.indexOf(subject); 156 entries = entries.plus(index, subject); 157 if (indexOf != -1) { 158 if (indexOf >= index) { 159 ++indexOf; 160 } 161 entries = entries.minus(indexOf); 162 } 163 return newWithRanks(entries); 164 } 165 166 @Override 167 public final RankLadder without(final SubjectRef<?> subject) { 168 final PVector<SubjectRef<?>> out = this.ranks().minus(subject); 169 return this.ranks() == out ? this : newWithRanks(out); 170 } 171 172 @Override 173 public abstract PVector<SubjectRef<?>> ranks(); 174 175 protected abstract RankLadder newWithRanks(final PVector<SubjectRef<?>> ents); 176 177 @Override 178 public final Component asComponent() { 179 return Component.text(build -> build.content(name()) 180 .decoration(TextDecoration.BOLD, true) 181 .hoverEvent(HoverEvent.showText(Messages.FORMATTER_BUTTON_INFO_PROMPT.tr())) 182 .clickEvent(ClickEvent.runCommand("/pex rank " + name()))); 183 } 184}