001/** 002 * 003 * Copyright 2020-2021 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.formtypes; 018 019import java.util.HashMap; 020import java.util.Map; 021import java.util.concurrent.ConcurrentHashMap; 022import java.util.logging.Logger; 023 024import org.jivesoftware.smack.util.Objects; 025import org.jivesoftware.smack.util.StringUtils; 026import org.jivesoftware.smack.util.XmlUtil; 027import org.jivesoftware.smackx.xdata.FormField; 028import org.jivesoftware.smackx.xdata.TextSingleFormField; 029import org.jivesoftware.smackx.xdata.packet.DataForm; 030 031public class FormFieldRegistry { 032 033 private static final Logger LOGGER = Logger.getLogger(FormFieldRegistry.class.getName()); 034 035 private static final Map<String, Map<String, FormField.Type>> REGISTRY = new HashMap<>(); 036 037 private static final Map<String, FormField.Type> CLARK_NOTATION_FIELD_REGISTRY = new ConcurrentHashMap<>(); 038 039 @SuppressWarnings("ReferenceEquality") 040 public static void register(DataForm dataForm) { 041 // TODO: Also allow forms of type 'result'? 042 if (dataForm.getType() != DataForm.Type.form) { 043 throw new IllegalArgumentException(); 044 } 045 046 String formType = null; 047 TextSingleFormField hiddenFormTypeField = dataForm.getHiddenFormTypeField(); 048 if (hiddenFormTypeField != null) { 049 formType = hiddenFormTypeField.getValue(); 050 } 051 052 for (FormField formField : dataForm.getFields()) { 053 // Note that we can compare here by reference equality to skip the hidden form type field. 054 if (formField == hiddenFormTypeField) { 055 continue; 056 } 057 058 FormField.Type type = formField.getType(); 059 if (type == FormField.Type.fixed) { 060 continue; 061 } 062 063 String fieldName = formField.getFieldName(); 064 register(formType, fieldName, type); 065 } 066 } 067 068 public static void register(String formType, String fieldName, FormField.Type fieldType) { 069 StringUtils.requireNotNullNorEmpty(fieldName, "fieldName must be provided"); 070 Objects.requireNonNull(fieldType); 071 072 if (formType == null) { 073 if (XmlUtil.isClarkNotation(fieldName)) { 074 CLARK_NOTATION_FIELD_REGISTRY.put(fieldName, fieldType); 075 } 076 return; 077 } 078 079 FormField.Type previousType; 080 synchronized (REGISTRY) { 081 Map<String, FormField.Type> fieldNameToType = REGISTRY.get(formType); 082 if (fieldNameToType == null) { 083 fieldNameToType = new HashMap<>(); 084 REGISTRY.put(formType, fieldNameToType); 085 } else { 086 previousType = fieldNameToType.get(fieldName); 087 if (previousType != null && previousType != fieldType) { 088 throw new IllegalArgumentException(); 089 } 090 } 091 previousType = fieldNameToType.put(fieldName, fieldType); 092 } 093 if (previousType != null && fieldType != previousType) { 094 LOGGER.warning("Form field registry inconsitency detected: Registered field '" + fieldName + "' of type " + fieldType + " but previous type was " + previousType); 095 } 096 097 } 098 099 public static FormField.Type lookup(String formType, String fieldName) { 100 if (formType == null) { 101 if (!XmlUtil.isClarkNotation(fieldName)) { 102 return null; 103 } 104 105 return CLARK_NOTATION_FIELD_REGISTRY.get(fieldName); 106 } 107 108 synchronized (REGISTRY) { 109 Map<String, FormField.Type> fieldNameToTypeMap = REGISTRY.get(formType); 110 if (fieldNameToTypeMap != null) { 111 FormField.Type type = fieldNameToTypeMap.get(fieldName); 112 if (type != null) { 113 return type; 114 } 115 } 116 } 117 118 return null; 119 } 120 121 public static synchronized FormField.Type lookup(String fieldName) { 122 return lookup(null, fieldName); 123 } 124 125}