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