001/* 002 * **** BEGIN LICENSE BLOCK ***** 003 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 004 * 005 * The contents of this file are subject to the Mozilla Public License Version 006 * 1.1 (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * http://www.mozilla.org/MPL/ 009 * 010 * Software distributed under the License is distributed on an "AS IS" basis, 011 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 012 * for the specific language governing rights and limitations under the 013 * License. 014 * 015 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in 016 * Java(TM), hosted at https://github.com/gunterze/dcm4che. 017 * 018 * The Initial Developer of the Original Code is 019 * Agfa Healthcare. 020 * Portions created by the Initial Developer are Copyright (C) 2014 021 * the Initial Developer. All Rights Reserved. 022 * 023 * Contributor(s): 024 * See @authors listed below 025 * 026 * Alternatively, the contents of this file may be used under the terms of 027 * either the GNU General Public License Version 2 or later (the "GPL"), or 028 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 029 * in which case the provisions of the GPL or the LGPL are applicable instead 030 * of those above. If you wish to allow use of your version of this file only 031 * under the terms of either the GPL or the LGPL, and not to allow others to 032 * use your version of this file under the terms of the MPL, indicate your 033 * decision by deleting the provisions above and replace them with the notice 034 * and other provisions required by the GPL or the LGPL. If you do not delete 035 * the provisions above, a recipient may use your version of this file under 036 * the terms of any one of the MPL, the GPL or the LGPL. 037 * 038 * ***** END LICENSE BLOCK ***** 039 */ 040package org.dcm4che3.conf.core.adapters; 041 042import org.dcm4che3.conf.core.api.*; 043import org.dcm4che3.conf.core.context.LoadingContext; 044import org.dcm4che3.conf.core.context.ProcessingContext; 045import org.dcm4che3.conf.core.context.SavingContext; 046import org.dcm4che3.conf.core.api.internal.*; 047import org.dcm4che3.conf.core.util.PathPattern; 048import org.slf4j.Logger; 049import org.slf4j.LoggerFactory; 050 051import java.util.HashMap; 052import java.util.Iterator; 053import java.util.Map; 054 055/** 056 * Default de/referencer. 057 */ 058public class DefaultReferenceAdapter implements ConfigTypeAdapter { 059 060 private static Logger log = LoggerFactory.getLogger(DefaultReferenceAdapter.class); 061 062 // generic uuid-based reference 063 private static final PathPattern uuidReferencePath = new PathPattern(Configuration.REFERENCE_BY_UUID_PATTERN); 064 065 private final Map<String, String> metadata = new HashMap<String, String>(); 066 067 public DefaultReferenceAdapter() { 068 metadata.put("type", "string"); 069 metadata.put("class", "Reference"); 070 } 071 072 @Override 073 public Object fromConfigNode(Object configNode, ConfigProperty property, LoadingContext ctx, Object parent) throws ConfigurationException { 074 075 // old deprecated style ref, for backwards-compatibility 076 if (configNode instanceof String) { 077 078 return resolveDeprecatedReference((String) configNode, property, ctx); 079 } 080 // new style 081 else { 082 String uuidRefStr = (String) ((Map) configNode).get(Configuration.REFERENCE_KEY); 083 084 String uuid = null; 085 try { 086 uuid = uuidReferencePath.parse(uuidRefStr).getParam("uuid"); 087 } catch (RuntimeException e) { 088 // in case if it's not a map or is null or has no property 089 throw new IllegalArgumentException("Unexpected value for reference property " + property.getAnnotatedName() + ", value" + configNode); 090 } 091 092 return getReferencedConfigurableObject(uuid, ctx, property); 093 } 094 } 095 096 private Object resolveDeprecatedReference(String configNode, ConfigProperty property, LoadingContext ctx) { 097 String refStr = configNode; 098 099 log.warn("Using deprecated reference format for configuration: " + refStr); 100 101 Configuration config = ctx.getTypeSafeConfiguration().getLowLevelAccess(); 102 Iterator search = config.search(refStr); 103 104 Map<String, Object> referencedNode = null; 105 if (search.hasNext()) 106 referencedNode = (Map<String, Object>) search.next(); 107 108 if (referencedNode == null) { 109 if (property.isWeakReference()) 110 return null; 111 else 112 throw new ConfigurationException("Referenced node '" + refStr + "' not found"); 113 } 114 115 // there is always uuid 116 String uuid; 117 try { 118 uuid = (String) referencedNode.get(Configuration.UUID_KEY); 119 if (uuid == null) throw new RuntimeException(); 120 } catch (RuntimeException e) { 121 throw new IllegalArgumentException("A referable node MUST have a UUID. A node referenced by " + refStr + " does not have UUID property."); 122 } 123 124 return getReferencedConfigurableObject(uuid, ctx, property); 125 } 126 127 128 @SuppressWarnings("unchecked") 129 private Object getReferencedConfigurableObject(String uuid, LoadingContext ctx, ConfigProperty property) { 130 Object byUUID = ctx.getTypeSafeConfiguration().findByUUID(uuid, property.getRawClass(), ctx); 131 132 if (byUUID == null && !property.isWeakReference()) { 133 134 ConfigurationException e = new ConfigurationException("Referenced node with uuid '" + uuid + "' not found"); 135 136 if (ctx.isIgnoreUnresolvedReferences()) { 137 log.error("Ignoring unresolved reference in configuration", e); 138 } else { 139 throw e; 140 } 141 } 142 143 return byUUID; 144 } 145 146 @Override 147 public Object toConfigNode(Object object, ConfigProperty property, SavingContext ctx) throws ConfigurationException { 148 Map<String, Object> node = Configuration.NodeFactory.emptyNode(); 149 150 ConfigProperty uuidPropertyForClass = ConfigReflection.getUUIDPropertyForClass(property.getRawClass()); 151 if (uuidPropertyForClass == null) 152 throw new ConfigurationException("Class " + property.getRawClass().getName() + " cannot be referenced, because it lacks a UUID property"); 153 154 String uuid; 155 uuid = (String) ConfigReflection.getProperty(object, uuidPropertyForClass); 156 node.put(Configuration.REFERENCE_KEY, uuidReferencePath.set("uuid", uuid).path()); 157 158 if (property.isWeakReference()) 159 node.put(Configuration.WEAK_REFERENCE_KEY, true); 160 161 return node; 162 } 163 164 @Override 165 public Map<String, Object> getSchema(ConfigProperty property, ProcessingContext ctx) throws ConfigurationException { 166 Map<String, Object> schema = new HashMap<String, Object>(); 167 schema.putAll(metadata); 168 schema.put("referencedClass", property.getRawClass().getSimpleName()); 169 return schema; 170 } 171 172 @Override 173 public Object normalize(Object configNode, ConfigProperty property, ProcessingContext ctx) throws ConfigurationException { 174 return configNode; 175 } 176 177}