001package ca.uhn.fhir.parser; 002 003import static org.apache.commons.lang3.StringUtils.isBlank; 004 005import ca.uhn.fhir.context.FhirContext; 006import ca.uhn.fhir.parser.json.JsonLikeValue.ScalarType; 007import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType; 008 009/* 010 * #%L 011 * HAPI FHIR - Core Library 012 * %% 013 * Copyright (C) 2014 - 2021 Smile CDR, Inc. 014 * %% 015 * Licensed under the Apache License, Version 2.0 (the "License"); 016 * you may not use this file except in compliance with the License. 017 * You may obtain a copy of the License at 018 * 019 * http://www.apache.org/licenses/LICENSE-2.0 020 * 021 * Unless required by applicable law or agreed to in writing, software 022 * distributed under the License is distributed on an "AS IS" BASIS, 023 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 024 * See the License for the specific language governing permissions and 025 * limitations under the License. 026 * #L% 027 */ 028 029/** 030 * The default error handler, which logs issues but does not abort parsing, with only one exception: 031 * <p> 032 * The {@link #invalidValue(ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation, String, String)} 033 * method will throw a {@link DataFormatException} by default since ignoring this type of error 034 * can lead to data loss (since invalid values are silently ignored). See 035 * {@link #setErrorOnInvalidValue(boolean)} for information on this. 036 * </p> 037 * 038 * @see IParser#setParserErrorHandler(IParserErrorHandler) 039 * @see FhirContext#setParserErrorHandler(IParserErrorHandler) 040 */ 041public class LenientErrorHandler extends BaseErrorHandler implements IParserErrorHandler { 042 043 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LenientErrorHandler.class); 044 private static final StrictErrorHandler STRICT_ERROR_HANDLER = new StrictErrorHandler(); 045 private boolean myErrorOnInvalidValue = true; 046 private boolean myLogErrors; 047 048 /** 049 * Constructor which configures this handler to log all errors 050 */ 051 public LenientErrorHandler() { 052 myLogErrors = true; 053 } 054 055 /** 056 * Constructor 057 * 058 * @param theLogErrors 059 * Should errors be logged? 060 * @since 1.2 061 */ 062 public LenientErrorHandler(boolean theLogErrors) { 063 myLogErrors = theLogErrors; 064 } 065 066 @Override 067 public void containedResourceWithNoId(IParseLocation theLocation) { 068 if (myLogErrors) { 069 ourLog.warn("Resource has contained child resource with no ID"); 070 } 071 } 072 073 @Override 074 public void incorrectJsonType(IParseLocation theLocation, String theElementName, ValueType theExpected, ScalarType theExpectedScalarType, ValueType theFound, ScalarType theFoundScalarType) { 075 if (myLogErrors) { 076 if (ourLog.isWarnEnabled()) { 077 String message = createIncorrectJsonTypeMessage(theElementName, theExpected, theExpectedScalarType, theFound, theFoundScalarType); 078 ourLog.warn(message); 079 } 080 } 081 } 082 083 @Override 084 public void invalidValue(IParseLocation theLocation, String theValue, String theError) { 085 if (isBlank(theValue) || myErrorOnInvalidValue == false) { 086 if (myLogErrors) { 087 ourLog.warn("{}Invalid attribute value \"{}\": {}", describeLocation(theLocation), theValue, theError); 088 } 089 } else { 090 STRICT_ERROR_HANDLER.invalidValue(theLocation, theValue, theError); 091 } 092 } 093 094 /** 095 * If set to <code>false</code> (default is <code>true</code>) invalid values will be logged. By 096 * default invalid attribute values cause this error handler to throw a {@link DataFormatException} (unlike 097 * other methods in this class which default to simply logging errors). 098 * <p> 099 * Note that empty values (e.g. <code>""</code>) will not lead to an error when this is set to 100 * <code>true</code>, only invalid values (e.g. a gender code of <code>foo</code>) 101 * </p> 102 * 103 * @see #setErrorOnInvalidValue(boolean) 104 */ 105 public boolean isErrorOnInvalidValue() { 106 return myErrorOnInvalidValue; 107 } 108 109 @Override 110 public void missingRequiredElement(IParseLocation theLocation, String theElementName) { 111 if (myLogErrors) { 112 ourLog.warn("Resource is missing required element: {}", theElementName); 113 } 114 } 115 116 /** 117 * If set to <code>false</code> (default is <code>true</code>) invalid values will be logged. By 118 * default invalid attribute values cause this error handler to throw a {@link DataFormatException} (unlike 119 * other methods in this class which default to simply logging errors). 120 * <p> 121 * Note that empty values (e.g. <code>""</code>) will not lead to an error when this is set to 122 * <code>true</code>, only invalid values (e.g. a gender code of <code>foo</code>) 123 * </p> 124 * 125 * @return Returns a reference to <code>this</code> for easy method chaining 126 * @see #isErrorOnInvalidValue() 127 */ 128 public LenientErrorHandler setErrorOnInvalidValue(boolean theErrorOnInvalidValue) { 129 myErrorOnInvalidValue = theErrorOnInvalidValue; 130 return this; 131 } 132 133 @Override 134 public void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName) { 135 if (myLogErrors) { 136 ourLog.warn("{}Multiple repetitions of non-repeatable element '{}' found while parsing", describeLocation(theLocation), theElementName); 137 } 138 } 139 140 @Override 141 public void unknownAttribute(IParseLocation theLocation, String theElementName) { 142 if (myLogErrors) { 143 ourLog.warn("{}Unknown attribute '{}' found while parsing",describeLocation(theLocation), theElementName); 144 } 145 } 146 147 @Override 148 public void unknownElement(IParseLocation theLocation, String theElementName) { 149 if (myLogErrors) { 150 ourLog.warn("{}Unknown element '{}' found while parsing", describeLocation(theLocation), theElementName); 151 } 152 } 153 154 @Override 155 public void unknownReference(IParseLocation theLocation, String theReference) { 156 if (myLogErrors) { 157 ourLog.warn("{}Resource has invalid reference: {}", describeLocation(theLocation), theReference); 158 } 159 } 160 161 @Override 162 public void extensionContainsValueAndNestedExtensions(IParseLocation theLocation) { 163 if (myLogErrors) { 164 ourLog.warn("{}Extension contains both a value and nested extensions", describeLocation(theLocation)); 165 } 166 } 167 168 public static String createIncorrectJsonTypeMessage(String theElementName, ValueType theExpected, ScalarType theExpectedScalarType, ValueType theFound, ScalarType theFoundScalarType) { 169 StringBuilder b = new StringBuilder(); 170 b.append("Found incorrect type for element "); 171 b.append(theElementName); 172 b.append(" - Expected "); 173 b.append(theExpected.name()); 174 if (theExpectedScalarType != null) { 175 b.append(" ("); 176 b.append(theExpectedScalarType.name()); 177 b.append(")"); 178 } 179 b.append(" and found "); 180 b.append(theFound.name()); 181 if (theFoundScalarType != null) { 182 b.append(" ("); 183 b.append(theFoundScalarType.name()); 184 b.append(")"); 185 } 186 String message = b.toString(); 187 return message; 188 } 189 190}