001/** 002 * 003 * Copyright 2020 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; 021 022import org.jivesoftware.smack.util.Objects; 023 024import org.jivesoftware.smackx.xdata.FormField; 025import org.jivesoftware.smackx.xdata.TextSingleFormField; 026import org.jivesoftware.smackx.xdata.packet.DataForm; 027 028public class FormFieldRegistry { 029 030 private static final Map<String, Map<String, FormField.Type>> REGISTRY = new HashMap<>(); 031 032 private static final Map<String, FormField.Type> LOOKASIDE_REGISTRY = new HashMap<>(); 033 034 private static final Map<String, String> FIELD_NAME_TO_FORM_TYPE = new HashMap<>(); 035 036 static { 037 register(FormField.FORM_TYPE, FormField.Type.hidden); 038 } 039 040 @SuppressWarnings("ReferenceEquality") 041 public static synchronized void register(DataForm dataForm) { 042 // TODO: Also allow forms of type 'result'? 043 if (dataForm.getType() != DataForm.Type.form) { 044 throw new IllegalArgumentException(); 045 } 046 047 String formType = null; 048 TextSingleFormField hiddenFormTypeField = dataForm.getHiddenFormTypeField(); 049 if (hiddenFormTypeField != null) { 050 formType = hiddenFormTypeField.getValue(); 051 } 052 053 for (FormField formField : dataForm.getFields()) { 054 // Note that we can compare here by reference equality to skip the hidden form type field. 055 if (formField == hiddenFormTypeField) { 056 continue; 057 } 058 059 String fieldName = formField.getFieldName(); 060 FormField.Type type = formField.getType(); 061 register(formType, fieldName, type); 062 } 063 } 064 065 public static synchronized void register(String formType, String fieldName, FormField.Type type) { 066 if (formType == null) { 067 FormFieldInformation formFieldInformation = lookup(fieldName); 068 if (formFieldInformation != null) { 069 if (Objects.equals(formType, formFieldInformation.formType) 070 && type.equals(formFieldInformation.formFieldType)) { 071 // The field is already registered, nothing to do here. 072 return; 073 } 074 075 String message = "There is already a field with the name'" + fieldName 076 + "' registered with the field type '" + formFieldInformation.formFieldType 077 + "', while this tries to register the field with the type '" + type + '\''; 078 throw new IllegalArgumentException(message); 079 } 080 081 LOOKASIDE_REGISTRY.put(fieldName, type); 082 return; 083 } 084 085 Map<String, FormField.Type> fieldNameToType = REGISTRY.get(formType); 086 if (fieldNameToType == null) { 087 fieldNameToType = new HashMap<>(); 088 REGISTRY.put(formType, fieldNameToType); 089 } else { 090 FormField.Type previousType = fieldNameToType.get(fieldName); 091 if (previousType != null && previousType != type) { 092 throw new IllegalArgumentException(); 093 } 094 } 095 fieldNameToType.put(fieldName, type); 096 097 FIELD_NAME_TO_FORM_TYPE.put(fieldName, formType); 098 } 099 100 public static synchronized void register(String fieldName, FormField.Type type) { 101 FormField.Type previousType = LOOKASIDE_REGISTRY.get(fieldName); 102 if (previousType != null) { 103 if (previousType == type) { 104 // Nothing to do here. 105 return; 106 } 107 throw new IllegalArgumentException("There is already a field with the name '" + fieldName 108 + "' registered with type " + previousType 109 + ", while trying to register this field with type '" + type + "'"); 110 } 111 112 LOOKASIDE_REGISTRY.put(fieldName, type); 113 } 114 115 public static synchronized FormField.Type lookup(String formType, String fieldName) { 116 if (formType != null) { 117 Map<String, FormField.Type> fieldNameToTypeMap = REGISTRY.get(formType); 118 if (fieldNameToTypeMap != null) { 119 FormField.Type type = fieldNameToTypeMap.get(fieldName); 120 if (type != null) { 121 return type; 122 } 123 } 124 } else { 125 formType = FIELD_NAME_TO_FORM_TYPE.get(fieldName); 126 if (formType != null) { 127 FormField.Type type = lookup(formType, fieldName); 128 if (type != null) { 129 return type; 130 } 131 } 132 } 133 134 // Fallback to lookaside registry. 135 return LOOKASIDE_REGISTRY.get(fieldName); 136 } 137 138 public static synchronized FormFieldInformation lookup(String fieldName) { 139 String formType = FIELD_NAME_TO_FORM_TYPE.get(fieldName); 140 FormField.Type type = lookup(formType, fieldName); 141 if (type == null) { 142 return null; 143 } 144 145 return new FormFieldInformation(type, formType); 146 } 147 148 public static final class FormFieldInformation { 149 public final FormField.Type formFieldType; 150 public final String formType; 151 152 153 private FormFieldInformation(FormField.Type formFieldType, String formType) { 154 this.formFieldType = formFieldType; 155 this.formType = formType; 156 } 157 } 158}