001/* 002 * Copyright 2015-2024 Ping Identity Corporation 003 * 004 * This program is free software; you can redistribute it and/or modify 005 * it under the terms of the GNU General Public License (GPLv2 only) 006 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 007 * as published by the Free Software Foundation. 008 * 009 * This program is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU General Public License for more details. 013 * 014 * You should have received a copy of the GNU General Public License 015 * along with this program; if not, see <http://www.gnu.org/licenses>. 016 */ 017 018package com.unboundid.scim2.server.utils; 019 020import com.fasterxml.jackson.databind.JsonNode; 021import com.fasterxml.jackson.databind.node.ArrayNode; 022import com.fasterxml.jackson.databind.node.ObjectNode; 023import com.unboundid.scim2.common.Path; 024import com.unboundid.scim2.common.annotations.NotNull; 025import com.unboundid.scim2.common.utils.JsonUtils; 026import com.unboundid.scim2.common.utils.SchemaUtils; 027 028import java.util.Iterator; 029import java.util.Map; 030 031 032 033/** 034 * An abstract class which may be implemented to trim resources down to 035 * selected attributes. 036 */ 037public abstract class ResourceTrimmer 038{ 039 /** 040 * Trim attributes of the object node to return. 041 * 042 * @param objectNode The object node to return. 043 * @return The trimmed object node ready to return to the client. 044 */ 045 @NotNull 046 public ObjectNode trimObjectNode(@NotNull final ObjectNode objectNode) 047 { 048 return trimObjectNode(objectNode, Path.root()); 049 } 050 051 /** 052 * Trim attributes of an inner object node to return. 053 * 054 * @param objectNode The object node to return. 055 * @param parentPath The parent path of attributes in the object. 056 * @return The trimmed object node ready to return to the client. 057 */ 058 @NotNull 059 private ObjectNode trimObjectNode(@NotNull final ObjectNode objectNode, 060 @NotNull final Path parentPath) 061 { 062 ObjectNode objectToReturn = JsonUtils.getJsonNodeFactory().objectNode(); 063 Iterator<Map.Entry<String, JsonNode>> i = objectNode.fields(); 064 while(i.hasNext()) 065 { 066 Map.Entry<String, JsonNode> field = i.next(); 067 final Path path; 068 if (parentPath.isRoot() && parentPath.getSchemaUrn() == null && 069 SchemaUtils.isUrn(field.getKey())) 070 { 071 path = Path.root(field.getKey()); 072 } 073 else 074 { 075 path = parentPath.attribute(field.getKey()); 076 } 077 078 if(path.isRoot() || shouldReturn(path)) 079 { 080 if (field.getValue().isArray()) 081 { 082 ArrayNode trimmedNode = trimArrayNode( 083 (ArrayNode) field.getValue(), path); 084 if(trimmedNode.size() > 0) 085 { 086 objectToReturn.set(field.getKey(), trimmedNode); 087 } 088 } 089 else if (field.getValue().isObject()) 090 { 091 ObjectNode trimmedNode = trimObjectNode( 092 (ObjectNode) field.getValue(), path); 093 if(trimmedNode.size() > 0) 094 { 095 objectToReturn.set(field.getKey(), trimmedNode); 096 } 097 } 098 else 099 { 100 objectToReturn.set(field.getKey(), field.getValue()); 101 } 102 } 103 } 104 return objectToReturn; 105 } 106 107 /** 108 * Trim attributes of the values in the array node to return. 109 * 110 * @param arrayNode The array node to return. 111 * @param parentPath The parent path of attributes in the array. 112 * @return The trimmed object node ready to return to the client. 113 */ 114 @NotNull 115 protected ArrayNode trimArrayNode(@NotNull final ArrayNode arrayNode, 116 @NotNull final Path parentPath) 117 { 118 ArrayNode arrayToReturn = JsonUtils.getJsonNodeFactory().arrayNode(); 119 for(JsonNode value : arrayNode) 120 { 121 if(value.isArray()) 122 { 123 ArrayNode trimmedNode = trimArrayNode((ArrayNode) value, parentPath); 124 if(trimmedNode.size() > 0) 125 { 126 arrayToReturn.add(trimmedNode); 127 } 128 } 129 else if(value.isObject()) 130 { 131 ObjectNode trimmedNode = trimObjectNode( 132 (ObjectNode) value, parentPath); 133 if(trimmedNode.size() > 0) 134 { 135 arrayToReturn.add(trimmedNode); 136 } 137 } 138 else 139 { 140 arrayToReturn.add(value); 141 } 142 } 143 return arrayToReturn; 144 } 145 146 /** 147 * Determine if the attribute specified by the path should be returned. 148 * 149 * @param path The path for the attribute. 150 * @return {@code true} to return the attribute or {@code false} to remove the 151 * attribute from the returned resource. 152 */ 153 public abstract boolean shouldReturn(@NotNull final Path path); 154}