001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.iso; 020 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.List; 024 025/** 026 * Mutable dataset implementation used by {@link ISODatasetField}. 027 */ 028public class ISODataset implements Dataset { 029 private final int identifier; 030 private final DatasetFormat format; 031 private final List<DatasetElement> elements = new ArrayList<>(); 032 033 /** 034 * Creates an empty dataset. 035 * 036 * @param identifier dataset identifier 037 * @param format dataset format 038 */ 039 public ISODataset(int identifier, DatasetFormat format) { 040 if (identifier < 0x01 || identifier > 0xFE) { 041 throw new IllegalArgumentException("dataset identifier out of range"); 042 } 043 this.identifier = identifier; 044 this.format = format; 045 } 046 047 @Override 048 public int getIdentifier() { 049 return identifier; 050 } 051 052 @Override 053 public DatasetFormat getFormat() { 054 return format; 055 } 056 057 /** 058 * Appends an element without replacing existing entries with the same id. 059 * 060 * @param element element to append 061 */ 062 public void addElement(DatasetElement element) { 063 elements.add(element); 064 } 065 066 /** 067 * Replaces any existing elements with the same id and then appends the supplied element. 068 * 069 * @param element element to store 070 */ 071 public void putElement(DatasetElement element) { 072 elements.removeIf(existing -> existing.getId() == element.getId()); 073 addElement(element); 074 } 075 076 /** 077 * Appends a primitive element. 078 * 079 * @param id element identifier 080 * @param component backing component 081 */ 082 public void addElement(int id, ISOComponent component) { 083 addElement(new DatasetElement(id, component)); 084 } 085 086 /** 087 * Replaces any existing element with the same id. 088 * 089 * @param id element identifier 090 * @param component backing component 091 */ 092 public void putElement(int id, ISOComponent component) { 093 putElement(new DatasetElement(id, component)); 094 } 095 096 /** 097 * Appends an element and records its constructed TLV flag. 098 * 099 * @param id element identifier 100 * @param component backing component 101 * @param constructed whether the tag is constructed 102 */ 103 public void addElement(int id, ISOComponent component, boolean constructed) { 104 addElement(new DatasetElement(id, component, constructed)); 105 } 106 107 /** 108 * Replaces any existing element with the same id and records its constructed TLV flag. 109 * 110 * @param id element identifier 111 * @param component backing component 112 * @param constructed whether the tag is constructed 113 */ 114 public void putElement(int id, ISOComponent component, boolean constructed) { 115 putElement(new DatasetElement(id, component, constructed)); 116 } 117 118 /** 119 * Removes all elements that match the supplied identifier. 120 * 121 * @param id element identifier to remove 122 */ 123 public void removeElement(int id) { 124 elements.removeIf(existing -> existing.getId() == id); 125 } 126 127 /** 128 * Indicates whether the dataset contains any elements. 129 * 130 * @return {@code true} when empty 131 */ 132 public boolean isEmpty() { 133 return elements.isEmpty(); 134 } 135 136 /** 137 * Stores a character element and returns this dataset for fluent chaining. 138 * 139 * @param id element identifier 140 * @param value element value 141 * @return this dataset 142 */ 143 public ISODataset with(int id, String value) { 144 ISOField field = new ISOField(id, value); 145 putElement(id, field); 146 return this; 147 } 148 149 /** 150 * Stores a binary element and returns this dataset for fluent chaining. 151 * 152 * @param id element identifier 153 * @param value element value 154 * @return this dataset 155 */ 156 public ISODataset with(int id, byte[] value) { 157 ISOBinaryField field = new ISOBinaryField(id, value); 158 putElement(id, field); 159 return this; 160 } 161 162 /** 163 * Stores an ISO component and returns this dataset for fluent chaining. 164 * 165 * @param id element identifier 166 * @param component backing component 167 * @return this dataset 168 */ 169 public ISODataset with(int id, ISOComponent component) { 170 component.setFieldNumber(id); 171 putElement(id, component); 172 return this; 173 } 174 175 /** 176 * Stores an ISO component and its constructed TLV flag, returning this dataset for fluent chaining. 177 * 178 * @param id element identifier 179 * @param component backing component 180 * @param constructed whether the tag is constructed 181 * @return this dataset 182 */ 183 public ISODataset with(int id, ISOComponent component, boolean constructed) { 184 component.setFieldNumber(id); 185 putElement(id, component, constructed); 186 return this; 187 } 188 189 @Override 190 public List<DatasetElement> getElements() { 191 return Collections.unmodifiableList(elements); 192 } 193 194 @Override 195 public List<DatasetElement> getElements(int id) { 196 List<DatasetElement> matches = new ArrayList<>(); 197 for (DatasetElement element : elements) { 198 if (element.getId() == id) { 199 matches.add(element); 200 } 201 } 202 return Collections.unmodifiableList(matches); 203 } 204 205 @Override 206 public DatasetElement getElement(int id) { 207 for (DatasetElement element : elements) { 208 if (element.getId() == id) { 209 return element; 210 } 211 } 212 return null; 213 } 214 215 /** 216 * Returns the {@link ISOComponent} of the first matching element. 217 * 218 * @param id element identifier 219 * @return matching component, or {@code null} if no element has the given id 220 */ 221 public ISOComponent getComponent(int id) { 222 DatasetElement element = getElement(id); 223 return element != null ? element.getComponent() : null; 224 } 225 226 /** 227 * Returns the logical value of the first matching element. 228 * 229 * @param id element identifier 230 * @return element value or {@code null} 231 * @throws ISOException on component access errors 232 */ 233 public Object getValue(int id) throws ISOException { 234 DatasetElement element = getElement(id); 235 return element != null ? element.getValue() : null; 236 } 237 238 /** 239 * Returns the bytes of the first matching element. 240 * 241 * @param id element identifier 242 * @return element bytes or {@code null} 243 * @throws ISOException on component access errors 244 */ 245 public byte[] getBytes(int id) throws ISOException { 246 DatasetElement element = getElement(id); 247 return element != null ? element.getBytes() : null; 248 } 249}