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.oid; 021 022import static org.hamcrest.CoreMatchers.is; 023import static org.hamcrest.CoreMatchers.not; 024import static org.hamcrest.CoreMatchers.nullValue; 025 026import java.io.IOException; 027import java.io.Serializable; 028 029import com.google.common.base.Objects; 030 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034import org.apache.isis.applib.services.bookmark.Bookmark; 035import org.apache.isis.core.commons.encoding.DataInputExtended; 036import org.apache.isis.core.commons.encoding.DataOutputExtended; 037import org.apache.isis.core.commons.ensure.Ensure; 038import org.apache.isis.core.commons.matchers.IsisMatchers; 039import org.apache.isis.core.commons.url.UrlEncodingUtils; 040import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException; 041import org.apache.isis.core.metamodel.adapter.version.Version; 042import org.apache.isis.core.metamodel.spec.ObjectSpecId; 043 044public final class RootOidDefault implements Serializable, RootOid { 045 046 private final static Logger LOG = LoggerFactory.getLogger(RootOidDefault.class); 047 048 private static final long serialVersionUID = 1L; 049 050 private final ObjectSpecId objectSpecId; 051 private final String identifier; 052 private final State state; 053 054 // not part of equality check 055 private Version version; 056 057 private int cachedHashCode; 058 059 060 061 // //////////////////////////////////////////// 062 // Constructor, factory methods 063 // //////////////////////////////////////////// 064 065 public static RootOidDefault createTransient(ObjectSpecId objectSpecId, final String identifier) { 066 return new RootOidDefault(objectSpecId, identifier, State.TRANSIENT); 067 } 068 069 public static RootOid create(Bookmark bookmark) { 070 return create(ObjectSpecId.of(bookmark.getObjectType()), bookmark.getIdentifier()); 071 } 072 073 public static RootOidDefault create(ObjectSpecId objectSpecId, final String identifier) { 074 return create(objectSpecId, identifier, null); 075 } 076 077 public static RootOidDefault create(ObjectSpecId objectSpecId, final String identifier, Long versionSequence) { 078 return create(objectSpecId, identifier, versionSequence, null, null); 079 } 080 081 public static RootOidDefault create(ObjectSpecId objectSpecId, final String identifier, Long versionSequence, String versionUser) { 082 return create(objectSpecId, identifier, versionSequence, versionUser, null); 083 } 084 085 public static RootOidDefault create(ObjectSpecId objectSpecId, final String identifier, Long versionSequence, Long versionUtcTimestamp) { 086 return create(objectSpecId, identifier, versionSequence, null, versionUtcTimestamp); 087 } 088 089 public static RootOidDefault create(ObjectSpecId objectSpecId, final String identifier, Long versionSequence, String versionUser, Long versionUtcTimestamp) { 090 return new RootOidDefault(objectSpecId, identifier, State.PERSISTENT, Version.create(versionSequence, versionUser, versionUtcTimestamp)); 091 } 092 093 094 095 public RootOidDefault(ObjectSpecId objectSpecId, final String identifier, final State state) { 096 this(objectSpecId, identifier, state, (Version)null); 097 } 098 099 public RootOidDefault(ObjectSpecId objectSpecId, final String identifier, final State state, Long versionSequence) { 100 this(objectSpecId, identifier, state, versionSequence, null, null); 101 } 102 103 /** 104 * If specify version sequence, can optionally specify the user that changed the object. This is used for informational purposes only. 105 */ 106 public RootOidDefault(ObjectSpecId objectSpecId, final String identifier, final State state, Long versionSequence, String versionUser) { 107 this(objectSpecId, identifier, state, versionSequence, versionUser, null); 108 } 109 110 /** 111 * If specify version sequence, can optionally specify utc timestamp that the oid was changed. This is used for informational purposes only. 112 */ 113 public RootOidDefault(ObjectSpecId objectSpecId, final String identifier, final State state, Long versionSequence, Long versionUtcTimestamp) { 114 this(objectSpecId, identifier, state, versionSequence, null, versionUtcTimestamp); 115 } 116 117 /** 118 * If specify version sequence, can optionally specify user and/or utc timestamp that the oid was changed. This is used for informational purposes only. 119 */ 120 public RootOidDefault(ObjectSpecId objectSpecId, final String identifier, final State state, Long versionSequence, String versionUser, Long versionUtcTimestamp) { 121 this(objectSpecId, identifier, state, Version.create(versionSequence, versionUser, versionUtcTimestamp)); 122 } 123 124 public RootOidDefault(ObjectSpecId objectSpecId, final String identifier, final State state, Version version) { 125 Ensure.ensureThatArg(objectSpecId, is(not(nullValue()))); 126 Ensure.ensureThatArg(identifier, is(not(nullValue()))); 127 Ensure.ensureThatArg(identifier, is(not(IsisMatchers.contains("#"))), "identifier '" + identifier + "' contains a '#' symbol"); 128 Ensure.ensureThatArg(identifier, is(not(IsisMatchers.contains("@"))), "identifier '" + identifier + "' contains an '@' symbol"); 129 Ensure.ensureThatArg(state, is(not(nullValue()))); 130 131 this.objectSpecId = objectSpecId; 132 this.identifier = identifier; 133 this.state = state; 134 this.version = version; 135 initialized(); 136 } 137 138 private void initialized() { 139 cacheState(); 140 } 141 142 143 // //////////////////////////////////////////// 144 // Encodeable 145 // //////////////////////////////////////////// 146 147 public RootOidDefault(final DataInputExtended input) throws IOException { 148 final String oidStr = input.readUTF(); 149 final RootOidDefault oid = getEncodingMarshaller().unmarshal(oidStr, RootOidDefault.class); 150 this.objectSpecId = oid.objectSpecId; 151 this.identifier = oid.identifier; 152 this.state = oid.state; 153 this.version = oid.version; 154 cacheState(); 155 } 156 157 @Override 158 public void encode(final DataOutputExtended output) throws IOException { 159 output.writeUTF(enString(getEncodingMarshaller())); 160 } 161 162 163 /** 164 * Cannot be a reference because Oid gets serialized by wicket viewer 165 */ 166 private OidMarshaller getEncodingMarshaller() { 167 return new OidMarshaller(); 168 } 169 170 171 // //////////////////////////////////////////// 172 // deString'able, enString 173 // //////////////////////////////////////////// 174 175 public static RootOid deStringEncoded(final String urlEncodedOidStr, OidMarshaller oidMarshaller) { 176 final String oidStr = UrlEncodingUtils.urlDecode(urlEncodedOidStr); 177 return deString(oidStr, oidMarshaller); 178 } 179 180 public static RootOidDefault deString(final String oidStr, OidMarshaller oidMarshaller) { 181 return oidMarshaller.unmarshal(oidStr, RootOidDefault.class); 182 } 183 184 @Override 185 public String enString(OidMarshaller oidMarshaller) { 186 return oidMarshaller.marshal(this); 187 } 188 189 @Override 190 public String enStringNoVersion(OidMarshaller oidMarshaller) { 191 return oidMarshaller.marshalNoVersion(this); 192 } 193 194 // //////////////////////////////////////////// 195 // Properties 196 // //////////////////////////////////////////// 197 198 @Override 199 public ObjectSpecId getObjectSpecId() { 200 return objectSpecId; 201 } 202 203 public String getIdentifier() { 204 return identifier; 205 } 206 207 @Override 208 public boolean isTransient() { 209 return state.isTransient(); 210 } 211 212 @Override 213 public boolean isViewModel() { 214 return state.isViewModel(); 215 } 216 217 @Override 218 public boolean isPersistent() { 219 return state.isPersistent(); 220 } 221 222 223 // //////////////////////////////////////////// 224 // asPersistent 225 // //////////////////////////////////////////// 226 227 @Override 228 public RootOidDefault asPersistent(String identifier) { 229 Ensure.ensureThatState(state.isTransient(), is(true)); 230 Ensure.ensureThatArg(identifier, is(not(nullValue()))); 231 232 return new RootOidDefault(objectSpecId, identifier, State.PERSISTENT); 233 } 234 235 236 // //////////////////////////////////////////// 237 // Version 238 // //////////////////////////////////////////// 239 240 public Version getVersion() { 241 return version; 242 } 243 244 @Override 245 public void setVersion(Version version) { 246 this.version = version; 247 } 248 249 @Override 250 public Comparison compareAgainst(RootOid other) { 251 if(!equals(other)) { 252 return Comparison.NOT_EQUIVALENT; 253 } 254 if(getVersion() == null || other.getVersion() == null) { 255 return Comparison.EQUIVALENT_BUT_NO_VERSION_INFO; 256 } 257 return getVersion().equals(other.getVersion()) 258 ? Comparison.EQUIVALENT_AND_UNCHANGED 259 : Comparison.EQUIVALENT_BUT_CHANGED; 260 } 261 262 // //////////////////////////////////////////// 263 // bookmark 264 // //////////////////////////////////////////// 265 266 @Override 267 public Bookmark asBookmark() { 268 final String objectType = getObjectSpecId().asString(); 269 final String identifier = getIdentifier(); 270 return new Bookmark(objectType, identifier); 271 } 272 273 // //////////////////////////////////////////// 274 // equals, hashCode 275 // //////////////////////////////////////////// 276 277 private void cacheState() { 278 cachedHashCode = 17; 279 cachedHashCode = 37 * cachedHashCode + objectSpecId.hashCode(); 280 cachedHashCode = 37 * cachedHashCode + identifier.hashCode(); 281 cachedHashCode = 37 * cachedHashCode + (isTransient() ? 0 : 1); 282 } 283 284 @Override 285 public boolean equals(final Object other) { 286 if (other == null) { 287 return false; 288 } 289 if (other == this) { 290 return true; 291 } 292 if (getClass() != other.getClass()) { 293 return false; 294 } 295 return equals((RootOid) other); 296 } 297 298 public boolean equals(final RootOid other) { 299 return Objects.equal(objectSpecId, other.getObjectSpecId()) && Objects.equal(identifier, other.getIdentifier()) && Objects.equal(isTransient(), other.isTransient()); 300 } 301 302 @Override 303 public int hashCode() { 304 return cachedHashCode; 305 } 306 307 308 // ///////////////////////////////////////////////////////// 309 // toString 310 // ///////////////////////////////////////////////////////// 311 312 @Override 313 public String toString() { 314 return enString(new OidMarshaller()); 315 } 316 317 318 319}