001/** 002 * Copyright 2014 Tampere University of Technology, Pori Department 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package core.tut.pori.http.parameters; 017 018import java.util.Arrays; 019import java.util.HashMap; 020import java.util.HashSet; 021import java.util.Iterator; 022import java.util.List; 023import java.util.Map.Entry; 024import java.util.Set; 025 026import org.apache.commons.lang3.StringUtils; 027import org.apache.log4j.Logger; 028 029import core.tut.pori.http.Definitions; 030 031/** 032 * Default dataGroups parser. 033 * 034 * Syntax: ?data_groups=DATA_GROUP_VALUE or with type ?data_groups=TYPE;DATA_GROUP_VALUE, these can be combined: ?data_groups=DATA_GROUP_VALUE,TYPE:DATA_GROUP_VALUE 035 * 036 * Note: this has no way of knowing if the given string are valid for the required context, you should check the values manually 037 * 038 * There is no concept of order for the data groups, and they can be provided in any order for the data_groups parameter. 039 */ 040public final class DataGroups extends HTTPParameter { 041 /** If present in a request, all available content should be returned in the response */ 042 public static final String DATA_GROUP_ALL = "all"; 043 /** If present in a request, the basic should be returned in the response. Note that the definition of "basic" is service specific. */ 044 public static final String DATA_GROUP_BASIC = "basic"; 045 /** If present in a request, the default content should be returned in the response. This in general equals to not providing a data group parameter. */ 046 public static final String DATA_GROUP_DEFAULTS = "defaults"; 047 /** the default HTTP parameter name */ 048 public static final String PARAMETER_DEFAULT_NAME = "data_groups"; 049 private static final Logger LOGGER = Logger.getLogger(DataGroups.class); 050 private HashMap<String, Set<String>> _dataGroups = null; // type-datagroup map, null = default, non-typed datagroups 051 052 @Override 053 public void initialize(List<String> parameterValues) throws IllegalArgumentException { 054 for(Iterator<String> iter = parameterValues.iterator();iter.hasNext();){ 055 parse(iter.next()); 056 } 057 } 058 059 @Override 060 public void initialize(String parameterValue) throws IllegalArgumentException{ 061 parse(parameterValue); 062 } 063 064 /** 065 * 066 * @param parameterValue 067 */ 068 private void parse(String parameterValue){ 069 if(StringUtils.isBlank(parameterValue)){ 070 LOGGER.debug("Detected null or empty value for parameter: "+getParameterName()); 071 return; 072 } 073 074 String[] parts = StringUtils.split(parameterValue, Definitions.SEPARATOR_URI_QUERY_TYPE_VALUE); 075 if(parts.length == 2){ // type;value 076 addDataGroup(parts[1], parts[0]); 077 }else if(parts.length == 1){ // only value 078 addDataGroup(parts[0]); 079 }else{ 080 throw new IllegalArgumentException("Invalid value "+parameterValue+" for paramater "+getParameterName()); 081 } 082 } 083 084 @Override 085 public boolean hasValues() { 086 return (_dataGroups != null); 087 } 088 089 /** 090 * varargs constructor for data groups 091 * 092 * @param dataGroups 093 */ 094 public DataGroups(String ...dataGroups) { 095 initialize(Arrays.asList(dataGroups)); 096 } 097 098 /** 099 * required for serialization 100 */ 101 public DataGroups(){ 102 // nothing needed 103 } 104 105 /** 106 * Create a new copy based on the given dataGroup, this will create a deep copy of the given object. 107 * 108 * @param dataGroups not-null 109 */ 110 public DataGroups(DataGroups dataGroups){ 111 if(dataGroups._dataGroups != null){ 112 _dataGroups = new HashMap<>(dataGroups._dataGroups.size()); 113 for(Entry<String,Set<String>> e : dataGroups._dataGroups.entrySet()){ 114 _dataGroups.put(e.getKey(), new HashSet<>(e.getValue())); 115 } 116 } 117 } 118 119 /** 120 * for sub-classing, use the static 121 * 122 * @param dataGroup 123 * @param type 124 * @return true if the given data group is given for the given type. If the type is not found, returns true if the data group is given by default (non-typed). 125 */ 126 protected boolean hasDataGroup(String dataGroup, String type){ 127 if(_dataGroups == null){ 128 LOGGER.debug("No datagroups."); 129 return false; 130 } 131 Set<String> dataGroups = _dataGroups.get(type); 132 if(dataGroups == null && type != null){ // did not find for the type, try if there are defaults 133 dataGroups = _dataGroups.get(null); 134 } 135 return (dataGroups == null ? false : dataGroups.contains(dataGroup)); 136 } 137 138 /** 139 * for sub-classing, use the static 140 * 141 * @return true if this group contains no data groups 142 */ 143 protected boolean isEmpty(){ 144 return (_dataGroups == null ? true : _dataGroups.isEmpty()); 145 } 146 147 /** 148 * 149 * @param dataGroup 150 * @param dataGroups can be null 151 * @return true if the data group contains the given group 152 */ 153 public static boolean hasDataGroup(String dataGroup, DataGroups dataGroups){ 154 if(dataGroups == null){ 155 return false; 156 }else{ 157 return dataGroups.hasDataGroup(dataGroup, (String)null); 158 } 159 } 160 161 /** 162 * 163 * @param dataGroup 164 * @param dataGroups can be null 165 * @param type 166 * @return true if the given data group is given for the given type and data group. If the type is not found, returns true if the data group is given by default (non-typed). 167 */ 168 public static boolean hasDataGroup(String dataGroup, DataGroups dataGroups, String type){ 169 if(dataGroups == null){ 170 return false; 171 }else{ 172 return dataGroups.hasDataGroup(dataGroup, type); 173 } 174 } 175 176 /** 177 * Remove the data group from the default type 178 * 179 * @param dataGroup 180 * @return true if the dataGroup was removed 181 */ 182 public boolean removeDataGroup(String dataGroup) { 183 return removeDataGroup(dataGroup, null); 184 } 185 186 /** 187 * Remove data group from the given type 188 * 189 * @param dataGroup 190 * @param type 191 * @return true if the group was removed (existed in this group) 192 */ 193 public boolean removeDataGroup(String dataGroup, String type){ 194 if(_dataGroups == null){ 195 return false; 196 } 197 Set<String> dataGroups = _dataGroups.get(type); 198 if(dataGroups == null){ 199 return false; 200 } 201 return dataGroups.remove(dataGroup); 202 } 203 204 /** 205 * Add datagroup to the default, non-typed datagroup list 206 * 207 * @param dataGroup 208 */ 209 public void addDataGroup(String dataGroup) { 210 addDataGroup(dataGroup, null); 211 } 212 213 /** 214 * Add the given dataGroup for the given type 215 * 216 * @param dataGroup 217 * @param type 218 */ 219 public void addDataGroup(String dataGroup, String type) { 220 if(_dataGroups == null){ 221 _dataGroups = new HashMap<>(); 222 } 223 Set<String> dataGroups = _dataGroups.get(type); 224 if(dataGroups == null){ 225 dataGroups = new HashSet<>(); 226 _dataGroups.put(type, dataGroups); 227 } 228 dataGroups.add(dataGroup); 229 } 230 231 /** 232 * 233 * @param dataGroups 234 * @return true if this group is empty 235 */ 236 public static boolean isEmpty(DataGroups dataGroups){ 237 if(dataGroups == null){ 238 return true; 239 }else{ 240 return !dataGroups.hasValues(); 241 } 242 } 243 244 /** 245 * 246 * @return this group as data group uri string 247 */ 248 public String toDataGroupString(){ 249 if(!hasValues()){ 250 return null; 251 } 252 StringBuilder sb = new StringBuilder(); 253 for(Iterator<Entry<String, Set<String>>> iter = _dataGroups.entrySet().iterator();;){ 254 Entry<String, Set<String>> e = iter.next(); 255 String type = e.getKey(); 256 Iterator<String> vIter = e.getValue().iterator(); 257 if(type != null){ // there is a type, we must separate all values to diffrent parts: TYPE:VALUE,TYPE:VALUE,TYPE:VALUE 258 do{ 259 sb.append(type); 260 sb.append(Definitions.SEPARATOR_URI_QUERY_TYPE_VALUE); 261 sb.append(vIter.next()); // there should always be at least one data group per type 262 }while(vIter.hasNext()); 263 }else{ // no type, we can just add the values: VALUE,VALUE,VALUE 264 sb.append(vIter.next()); // there should always be at least one data group per type 265 while(vIter.hasNext()){ 266 sb.append(Definitions.SEPARATOR_URI_QUERY_PARAM_VALUES); 267 sb.append(vIter.next()); 268 } 269 } 270 271 if(iter.hasNext()){ 272 sb.append(Definitions.SEPARATOR_URI_QUERY_PARAM_VALUES); 273 }else{ 274 break; 275 } 276 } 277 278 return sb.toString(); 279 } 280 281 /** 282 * @return the datagroups as datagroup String or null if no datagroups 283 */ 284 @Override 285 public String getValue() { 286 return toDataGroupString(); 287 } 288}