001package ca.uhn.fhir.model.api; 002 003import static org.apache.commons.lang3.StringUtils.isBlank; 004import static org.apache.commons.lang3.StringUtils.isNotBlank; 005 006import java.io.Serializable; 007 008import org.apache.commons.lang3.builder.ToStringBuilder; 009 010/* 011 * #%L 012 * HAPI FHIR - Core Library 013 * %% 014 * Copyright (C) 2014 - 2017 University Health Network 015 * %% 016 * Licensed under the Apache License, Version 2.0 (the "License"); 017 * you may not use this file except in compliance with the License. 018 * You may obtain a copy of the License at 019 * 020 * http://www.apache.org/licenses/LICENSE-2.0 021 * 022 * Unless required by applicable law or agreed to in writing, software 023 * distributed under the License is distributed on an "AS IS" BASIS, 024 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 025 * See the License for the specific language governing permissions and 026 * limitations under the License. 027 * #L% 028 */ 029 030/** 031 * Represents a FHIR resource path specification, e.g. <code>Patient:name</code> 032 * <p> 033 * Note on equality: This class uses {@link #getValue() value} and the {@link #isRecurse() recurse} properties to test 034 * equality. Prior to HAPI 1.2 (and FHIR DSTU2) the recurse property did not exist, so this may merit consideration when 035 * upgrading servers. 036 * </p> 037 */ 038public class Include implements Serializable { 039 040 private static final long serialVersionUID = 1L; 041 042 private final boolean myImmutable; 043 private boolean myRecurse; 044 private String myValue; 045 046 /** 047 * Constructor for <b>non-recursive</b> include 048 * 049 * @param theValue 050 * The <code>_include</code> value, e.g. "Patient:name" 051 */ 052 public Include(String theValue) { 053 myValue = theValue; 054 myImmutable = false; 055 } 056 057 /** 058 * Constructor for an include 059 * 060 * @param theValue 061 * The <code>_include</code> value, e.g. "Patient:name" 062 * @param theRecurse 063 * Should the include recurse 064 */ 065 public Include(String theValue, boolean theRecurse) { 066 myValue = theValue; 067 myRecurse = theRecurse; 068 myImmutable = false; 069 } 070 071 /** 072 * Constructor for an include 073 * 074 * @param theValue 075 * The <code>_include</code> value, e.g. "Patient:name" 076 * @param theRecurse 077 * Should the include recurse 078 */ 079 public Include(String theValue, boolean theRecurse, boolean theImmutable) { 080 myValue = theValue; 081 myRecurse = theRecurse; 082 myImmutable = theImmutable; 083 } 084 085 /** 086 * Creates a copy of this include with non-recurse behaviour 087 */ 088 public Include asNonRecursive() { 089 return new Include(myValue, false); 090 } 091 092 /** 093 * Creates a copy of this include with recurse behaviour 094 */ 095 public Include asRecursive() { 096 return new Include(myValue, true); 097 } 098 099 /** 100 * See the note on equality on the {@link Include class documentation} 101 */ 102 @Override 103 public boolean equals(Object obj) { 104 if (this == obj) { 105 return true; 106 } 107 if (obj == null) { 108 return false; 109 } 110 if (getClass() != obj.getClass()) { 111 return false; 112 } 113 Include other = (Include) obj; 114 if (myRecurse != other.myRecurse) { 115 return false; 116 } 117 if (myValue == null) { 118 if (other.myValue != null) { 119 return false; 120 } 121 } else if (!myValue.equals(other.myValue)) { 122 return false; 123 } 124 return true; 125 } 126 127 /** 128 * Returns the portion of the value before the first colon 129 */ 130 public String getParamType() { 131 int firstColon = myValue.indexOf(':'); 132 if (firstColon == -1 || firstColon == myValue.length() - 1) { 133 return null; 134 } 135 return myValue.substring(0, firstColon); 136 } 137 138 /** 139 * Returns the portion of the value after the first colon but before the second colon 140 */ 141 public String getParamName() { 142 int firstColon = myValue.indexOf(':'); 143 if (firstColon == -1 || firstColon == myValue.length() - 1) { 144 return null; 145 } 146 int secondColon = myValue.indexOf(':', firstColon + 1); 147 if (secondColon != -1) { 148 return myValue.substring(firstColon + 1, secondColon); 149 } 150 return myValue.substring(firstColon + 1); 151 } 152 153 /** 154 * Returns the portion of the string after the second colon, or null if there are not two colons in the value. 155 */ 156 public String getParamTargetType() { 157 int firstColon = myValue.indexOf(':'); 158 if (firstColon == -1 || firstColon == myValue.length() - 1) { 159 return null; 160 } 161 int secondColon = myValue.indexOf(':', firstColon + 1); 162 if (secondColon != -1) { 163 return myValue.substring(secondColon + 1); 164 } 165 return null; 166 167 } 168 169 public String getValue() { 170 return myValue; 171 } 172 173 /** 174 * See the note on equality on the {@link Include class documentation} 175 */ 176 @Override 177 public int hashCode() { 178 final int prime = 31; 179 int result = 1; 180 result = prime * result + (myRecurse ? 1231 : 1237); 181 result = prime * result + ((myValue == null) ? 0 : myValue.hashCode()); 182 return result; 183 } 184 185 /** 186 * Is this object {@link #toLocked() locked}? 187 */ 188 public boolean isLocked() { 189 return myImmutable; 190 } 191 192 public boolean isRecurse() { 193 return myRecurse; 194 } 195 196 public void setRecurse(boolean theRecurse) { 197 myRecurse = theRecurse; 198 } 199 200 public void setValue(String theValue) { 201 if (myImmutable) { 202 throw new IllegalStateException("Can not change the value of this include"); 203 } 204 myValue = theValue; 205 } 206 207 /** 208 * Return a new 209 */ 210 public Include toLocked() { 211 Include retVal = new Include(myValue, myRecurse, true); 212 return retVal; 213 } 214 215 @Override 216 public String toString() { 217 ToStringBuilder builder = new ToStringBuilder(this); 218 builder.append("value", myValue); 219 builder.append("recurse", myRecurse); 220 return builder.toString(); 221 } 222 223 /** 224 * Creates and returns a new copy of this Include with the given type. The following table shows what will be 225 * returned: 226 * <table> 227 * <tr> 228 * <th>Initial Contents</th> 229 * <th>theResourceType</th> 230 * <th>Output</th> 231 * </tr> 232 * <tr> 233 * <td>Patient:careProvider</th> 234 * <th>Organization</th> 235 * <th>Patient:careProvider:Organization</th> 236 * </tr> 237 * <tr> 238 * <td>Patient:careProvider:Practitioner</th> 239 * <th>Organization</th> 240 * <th>Patient:careProvider:Organization</th> 241 * </tr> 242 * <tr> 243 * <td>Patient</th> 244 * <th>(any)</th> 245 * <th>{@link IllegalStateException}</th> 246 * </tr> 247 * </table> 248 * 249 * @param theResourceType 250 * The resource type (e.g. "Organization") 251 * @return A new copy of the include. Note that if this include is {@link #toLocked() locked}, the returned include 252 * will be too 253 */ 254 public Include withType(String theResourceType) { 255 StringBuilder b = new StringBuilder(); 256 257 String paramType = getParamType(); 258 String paramName = getParamName(); 259 if (isBlank(paramType) || isBlank(paramName)) { 260 throw new IllegalStateException("This include does not contain a value in the format [ResourceType]:[paramName]"); 261 } 262 b.append(paramType); 263 b.append(":"); 264 b.append(paramName); 265 266 if (isNotBlank(theResourceType)) { 267 b.append(':'); 268 b.append(theResourceType); 269 } 270 Include retVal = new Include(b.toString(), myRecurse, myImmutable); 271 return retVal; 272 } 273 274}