001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020package org.apache.isis.core.metamodel.adapter; 021 022import static org.apache.isis.core.metamodel.adapter.ResolveState.RepresentsPersistent.DOES_NOT_REPRESENT_PERSISTENT; 023import static org.apache.isis.core.metamodel.adapter.ResolveState.RepresentsPersistent.REPRESENTS_PERSISTENT; 024import static org.apache.isis.core.metamodel.adapter.ResolveState.RespondsToChanges.DOES_NOT_RESPOND_TO_CHANGES; 025import static org.apache.isis.core.metamodel.adapter.ResolveState.RespondsToChanges.RESPONDS_TO_CHANGES; 026import static org.apache.isis.core.metamodel.adapter.ResolveState.TransitionFrom.CANNOT_TRANSITION_FROM; 027import static org.apache.isis.core.metamodel.adapter.ResolveState.TransitionFrom.CAN_TRANSITION_FROM; 028 029import java.util.Arrays; 030import java.util.HashMap; 031import java.util.HashSet; 032import java.util.List; 033import java.util.Map; 034 035import com.google.common.collect.Maps; 036 037public final class ResolveState { 038 private static final Map<String, ResolveState> statesByName = Maps.newHashMap(); 039 040 static enum TransitionFrom { 041 CAN_TRANSITION_FROM, CANNOT_TRANSITION_FROM 042 } 043 044 static enum RespondsToChanges { 045 RESPONDS_TO_CHANGES, DOES_NOT_RESPOND_TO_CHANGES 046 } 047 048 static enum RepresentsPersistent { 049 REPRESENTS_PERSISTENT, DOES_NOT_REPRESENT_PERSISTENT 050 } 051 052 /** 053 * When first instantiated by <tt>PojoAdapterFactory</tt>. 054 */ 055 public static final ResolveState NEW = new ResolveState("New", "N~~", null, CANNOT_TRANSITION_FROM, DOES_NOT_RESPOND_TO_CHANGES, DOES_NOT_REPRESENT_PERSISTENT); 056 public static final ResolveState TRANSIENT = new ResolveState("Transient", "T~~", null, CANNOT_TRANSITION_FROM, DOES_NOT_RESPOND_TO_CHANGES, DOES_NOT_REPRESENT_PERSISTENT); 057 public static final ResolveState GHOST = new ResolveState("Ghost", "PG~", null, CAN_TRANSITION_FROM, RESPONDS_TO_CHANGES, REPRESENTS_PERSISTENT); 058 public static final ResolveState RESOLVED = new ResolveState("Resolved", "PR~", null, CAN_TRANSITION_FROM, RESPONDS_TO_CHANGES, REPRESENTS_PERSISTENT); 059 public static final ResolveState RESOLVING = new ResolveState("Resolving", "Pr~", RESOLVED, CANNOT_TRANSITION_FROM, DOES_NOT_RESPOND_TO_CHANGES, REPRESENTS_PERSISTENT); 060 public static final ResolveState UPDATING = new ResolveState("Updating", "PU~", RESOLVED, CANNOT_TRANSITION_FROM, DOES_NOT_RESPOND_TO_CHANGES, REPRESENTS_PERSISTENT); 061 public static final ResolveState DESTROYED = new ResolveState("Destroyed", "D~~", null, CANNOT_TRANSITION_FROM, RESPONDS_TO_CHANGES, DOES_NOT_REPRESENT_PERSISTENT); 062 public static final ResolveState VALUE = new ResolveState("Value", "V~~", null, CANNOT_TRANSITION_FROM, RESPONDS_TO_CHANGES, DOES_NOT_REPRESENT_PERSISTENT); 063 064 // 20120709: used only in <tt>Memento</tt>, when recreating a transient object. 065 // however, analysis is that could equally set to TRANSIENT, rendering this state 066 // surplus to requirements. 067 // public static final ResolveState SERIALIZING_TRANSIENT = new ResolveState("Serializing Transient", "T~S", TRANSIENT, CANNOT_TRANSITION_FROM, DOES_NOT_RESPOND_TO_CHANGES, REPRESENTS_TRANSIENT, DOES_NOT_REPRESENT_PERSISTENT); 068 069 // no longer seem to be used 070 071 // public static final ResolveState PART_RESOLVED = new 072 // ResolveState("Part Resolved", "Pr~~", null, RESOLVABLE_FROM, 073 // NOT_RESOLVABLE_INTO, RESPONDS_TO_CHANGES, DOES_NOT_REPRESENT_TRANSIENT, 074 // REPRESENTS_PERSISTENT, DOES_NOT_REPRESENT_RESOLVING, COULD_RESOLVE); 075 // public static final ResolveState RESOLVING_PART = new 076 // ResolveState("Resolving Part", "P~r~", PART_RESOLVED, 077 // NOT_RESOLVABLE_FROM, RESOLVABLE_INTO, DOES_NOT_RESPOND_TO_CHANGES, 078 // DOES_NOT_REPRESENT_TRANSIENT, REPRESENTS_PERSISTENT, 079 // REPRESENTS_RESOLVING, COULD_RESOLVE); 080 081 // no longer appear to be needed following the removal of remoting support. 082 083 // public static final ResolveState SERIALIZING_GHOST = new 084 // ResolveState("Serializing Ghost", "PG~S", GHOST, NOT_RESOLVABLE_FROM, 085 // NOT_RESOLVABLE_INTO, DOES_NOT_RESPOND_TO_CHANGES, 086 // DOES_NOT_REPRESENT_TRANSIENT, REPRESENTS_PERSISTENT, 087 // DOES_NOT_REPRESENT_RESOLVING, COULD_RESOLVE); 088 // public static final ResolveState SERIALIZING_PART_RESOLVED = new 089 // ResolveState("Serializing Part Resolved", "Pr~S", PART_RESOLVED, 090 // NOT_RESOLVABLE_FROM, NOT_RESOLVABLE_INTO, DOES_NOT_RESPOND_TO_CHANGES, 091 // DOES_NOT_REPRESENT_TRANSIENT, REPRESENTS_PERSISTENT, 092 // DOES_NOT_REPRESENT_RESOLVING, 093 // COULD_RESOLVE); 094 095 // 20120709: only used in <tt>DefaultPersistAlgorithm</tt> 096 // able to remove because, after refactoring simplifications, ended up as equivalent to UPDATING. 097 // public static final ResolveState SERIALIZING_RESOLVED = new ResolveState("Serializing Resolved", "PRS", RESOLVED, CANNOT_TRANSITION_FROM, DOES_NOT_RESPOND_TO_CHANGES, DOES_NOT_REPRESENT_TRANSIENT, REPRESENTS_PERSISTENT); 098 099 100 /** 101 * These cannot be passed into the constructor because cannot reference an 102 * instance until it has been declared. 103 */ 104 public static Map<ResolveState, ResolveState[]> changeToStatesByState = new HashMap<ResolveState, ResolveState[]>() { 105 private static final long serialVersionUID = 1L; 106 { 107 // previously also RESOLVING_PART and SERIALIZING_GHOST 108 put(GHOST, new ResolveState[] { DESTROYED, RESOLVING, UPDATING }); 109 put(NEW, new ResolveState[] { TRANSIENT, GHOST, VALUE }); 110 // previously also SERIALIZING_TRANSIENT 111 put(TRANSIENT, new ResolveState[] { RESOLVED }); 112 put(RESOLVING, new ResolveState[] { RESOLVED }); 113 // previously also SERIALIZING_RESOLVED 114 put(RESOLVED, new ResolveState[] { GHOST, UPDATING, DESTROYED }); 115 //put(SERIALIZING_RESOLVED, new ResolveState[] { RESOLVED }); 116 //put(SERIALIZING_TRANSIENT, new ResolveState[] { TRANSIENT }); 117 put(UPDATING, new ResolveState[] { RESOLVED }); 118 put(DESTROYED, new ResolveState[] {}); 119 put(VALUE, new ResolveState[] {}); 120 121 // put(PART_RESOLVED, new ResolveState[] { RESOLVING , UPDATING, 122 // DESTROYED , RESOLVING_PART, SERIALIZING_PART_RESOLVED }); 123 // put(RESOLVING_PART, new ResolveState[] { PART_RESOLVED, RESOLVED 124 // }); 125 // put(SERIALIZING_GHOST, new ResolveState[] { GHOST }); 126 // put(SERIALIZING_PART_RESOLVED, new ResolveState[] { PART_RESOLVED 127 // }); 128 129 } 130 }; 131 132 private final String code; 133 private final ResolveState endState; 134 private final String name; 135 private final TransitionFrom transitionFrom; 136 private final RespondsToChanges respondsToChanges; 137 private final RepresentsPersistent representsPersistent; 138 private HashSet<ResolveState> changeToStates; 139 140 private ResolveState(final String name, final String code, final ResolveState endState, final TransitionFrom transitionFrom, final RespondsToChanges respondsToChanges, final RepresentsPersistent representsPersistent) { 141 this.name = name; 142 this.code = code; 143 this.endState = endState; 144 this.transitionFrom = transitionFrom; 145 this.respondsToChanges = respondsToChanges; 146 this.representsPersistent = representsPersistent; 147 statesByName.put(name, this); 148 } 149 150 /** 151 * Four character representation of the state. 152 * 153 * <p> 154 * The format is <tt>XYZ</tt> where: 155 * <ul> 156 * <li><tt>X</tt> is transient state: 157 * <ul> 158 * <li>N</li> for <b>N</b>ew 159 * <li>T</li> for <b>T</b>ransient 160 * <li>P</li> for <b>P</b>ersistent 161 * <li>D</li> for <b>D</b>estroyed 162 * <li>V</li> for <b>V</b>alue 163 * </ul> 164 * </li> 165 * <li><tt>Y</tt> (for persistent only) is the resolve state: 166 * <ul> 167 * <li>G</li> for <b>G</b>host 168 * <li>R</li> for <b>R</b>esolved 169 * <li>r</li> for <b>r</b>esolving 170 * <li>~</li> if not persistent 171 * </ul> 172 * </li> 173 * <li><tt>Z</tt> (for non-standalone, not resolving, not updating, not 174 * destroyed) is the serialization state: 175 * <ul> 176 * <li>~</li> not serializing 177 * <li>S</li> is serializing 178 * </ul> 179 * </li> 180 * </ul> 181 */ 182 public String code() { 183 return code; 184 } 185 186 public ResolveState getEndState() { 187 return endState; 188 } 189 190 /** 191 * Returns <tt>true</tt> when an object is persistent (except for 192 * {@link #VALUE} adapters). 193 * 194 * <p> 195 * Always returns <tt>false</tt> for {@link #VALUE}. 196 */ 197 public boolean representsPersistent() { 198 return this.representsPersistent == RepresentsPersistent.REPRESENTS_PERSISTENT; 199 } 200 201 /** 202 * As per {@link #isValidToChangeTo(ResolveState)}, but will additionally 203 * return <tt>false</tt> if the current state can never be transitioned from. 204 */ 205 public boolean canTransitionToResolving() { 206 if (this.transitionFrom != CAN_TRANSITION_FROM) { 207 return false; 208 } 209 return isValidToChangeTo(ResolveState.RESOLVING); 210 } 211 212 /** 213 * Returns false while object is having its field set up. 214 */ 215 public boolean respondToChangesInPersistentObjects() { 216 return respondsToChanges == RESPONDS_TO_CHANGES; 217 } 218 219 public boolean isNew() { 220 return this == NEW; 221 } 222 223 public boolean isValue() { 224 return this == VALUE; 225 } 226 227 public boolean isTransient() { 228 return this == TRANSIENT; 229 } 230 231 public boolean isGhost() { 232 return this == GHOST; 233 } 234 235 public boolean isUpdating() { 236 return this == UPDATING; 237 } 238 239 public boolean isResolved() { 240 return this == RESOLVED; 241 } 242 243 public boolean isResolving() { 244 return this == RESOLVING; 245 } 246 247 public boolean isDestroyed() { 248 return this == DESTROYED; 249 } 250 251 252 /** 253 * Determines if the resolved state can be changed from this state to the 254 * specified state. Returns true if the change is valid. 255 */ 256 public boolean isValidToChangeTo(final ResolveState nextState) { 257 cacheChangeToStatesIfNecessary(); 258 return this.changeToStates.contains(nextState); 259 } 260 261 private void cacheChangeToStatesIfNecessary() { 262 if (this.changeToStates == null) { 263 final List<ResolveState> nextStates = Arrays.asList(changeToStatesByState.get(this)); 264 changeToStates = new HashSet<ResolveState>(nextStates); 265 } 266 } 267 268 public String name() { 269 return name; 270 } 271 272 private transient String cachedToString; 273 274 @Override 275 public String toString() { 276 if (cachedToString == null) { 277 final StringBuffer str = new StringBuffer(); 278 str.append("ResolveState [name="); 279 str.append(name); 280 str.append(",code="); 281 str.append(code); 282 if (endState != null) { 283 str.append(",endstate="); 284 str.append(endState.name()); 285 } 286 str.append("]"); 287 cachedToString = str.toString(); 288 } 289 return cachedToString; 290 } 291 292}