/protocols/smpp/src/main/java/org/mobicents/protocols/smpp/gsm/UserDataImpl.java
Java | 185 lines | 121 code | 14 blank | 50 comment | 26 complexity | 357b94e4cdbe44ef42794c223ebca1fb MD5 | raw file
1/* 2 * JBoss, Home of Professional Open Source 3 * Copyright 2011, Red Hat, Inc. and individual contributors 4 * by the @authors tag. See the copyright.txt in the distribution for a 5 * full listing of individual contributors. 6 * 7 * This is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU Lesser General Public License as 9 * published by the Free Software Foundation; either version 2.1 of 10 * the License, or (at your option) any later version. 11 * 12 * This software is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this software; if not, write to the Free 19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 21 */ 22 23package org.mobicents.protocols.smpp.gsm; 24 25import java.nio.ByteBuffer; 26import java.util.ArrayList; 27import java.util.Collections; 28import java.util.LinkedList; 29import java.util.List; 30 31/** 32 * Implementation of {@link UserData}. 33 * 34 * <p>This implementation automatically handles the inclusion of the 35 * concatenated SMS {@link HeaderElement}. Calling code should <strong>not 36 * </strong> attempt to add a {@link ConcatenatedSms} header element 37 * via {@link #addHeaderElement(HeaderElement)}.</p> 38 * @version $Id: UserDataImpl.java 486 2010-02-15 10:48:15Z orank $ 39 */ 40public class UserDataImpl implements UserData { 41 42 private LinkedList<HeaderElement> headerElements = new LinkedList<HeaderElement>(); 43 private byte[] data; 44 private boolean useConcat16; 45 46 /** 47 * Create a new <tt>UserDataImpl</tt> that uses 8-bit reference numbers, 48 * if concatenated SMS is required. 49 */ 50 public UserDataImpl() { 51 } 52 53 /** 54 * Create a new <tt>UserDataImpl</tt>. 55 * @param useConcat16 If concatenated SMS is required, pass <tt>true</tt> 56 * for this value to use 16-bit segment reference numbers or <tt>false</tt> 57 * to use 8-bit segment reference numbers. 58 */ 59 public UserDataImpl(boolean useConcat16) { 60 this.useConcat16 = useConcat16; 61 } 62 63 /** 64 * {@inheritDoc} 65 * @throws IllegalArgumentException If <tt>element</tt> is an instance of 66 * {@link ConcatenatedSms}. 67 */ 68 public void addHeaderElement(HeaderElement element) { 69 if (element instanceof ConcatenatedSms) { 70 throw new IllegalArgumentException( 71 "Concatenated SMS is handled automatically."); 72 } 73 headerElements.add(element); 74 Collections.sort(headerElements, new HeaderElementComparator()); 75 } 76 77 public byte[] toSingleSms() { 78 byte[][] segments = toSegments(); 79 if (segments.length > 1) { 80 throw new IllegalStateException( 81 "There is more than one message segment"); 82 } 83 return segments[0]; 84 } 85 86 public byte[][] toSegments() { 87 List<ByteBuffer> segments = new ArrayList<ByteBuffer>(); 88 List<HeaderElement> elements; 89 if (calcSize(headerElements, data) > 140) { 90 // Concatenation is required. 91 elements = dupElements(); 92 HeaderElement concat = new ConcatenatedSms(useConcat16); 93 elements.add(0, concat); 94 } else { 95 elements = headerElements; 96 } 97 ByteBuffer dataBuffer; 98 if (data != null) { 99 dataBuffer = ByteBuffer.wrap(data); 100 } else { 101 dataBuffer = ByteBuffer.wrap(new byte[0]); 102 } 103 boolean needMoreSegments = true; 104 boolean needUdhl = elements.size() > 0; 105 int segmentNum = 0; 106 while (needMoreSegments) { 107 segmentNum++; 108 ByteBuffer segment = ByteBuffer.allocate(140); 109 if (needUdhl) { 110 segment.put((byte) 0); 111 } 112 boolean allComplete = true; 113 for (HeaderElement element : elements) { 114 if (element.isRecurring() || !element.isComplete()) { 115 element.write(segmentNum, segment); 116 } 117 allComplete &= element.isComplete(); 118 if (segment.remaining() < 2) { 119 break; 120 } 121 } 122 int headerSize = (segment.capacity() - segment.remaining()) - 1; 123 segment.put(0, (byte) headerSize); 124 if (segment.remaining() > 0) { 125 int numBytes = 126 Math.min(segment.remaining(), dataBuffer.remaining()); 127 dataBuffer.get(segment.array(), segment.position(), numBytes); 128 segment.position(segment.position() + numBytes); 129 } 130 segments.add(segment); 131 if (allComplete && dataBuffer.remaining() == 0) { 132 needMoreSegments = false; 133 } 134 } 135 for (HeaderElement element : elements) { 136 element.postProcess(segments); 137 } 138 byte[][] result = new byte[segments.size()][]; 139 for (int i = 0; i < segments.size(); i++) { 140 ByteBuffer segment = segments.get(i); 141 // If the segment was completely filled, just use the ByteBuffer's 142 // backing array. If it wasn't filled, copy out the exact 143 // number of bytes that were filled into the array. 144 if (segment.remaining() == 0) { 145 result[i] = segment.array(); 146 } else { 147 segment.flip(); 148 result[i] = new byte[segment.remaining()]; 149 segment.get(result[i], 0, result[i].length); 150 } 151 } 152 return result; 153 } 154 155 public boolean isMultiMessage() { 156 return calcSize(headerElements, data) > 140; 157 } 158 159 public byte[] getData() { 160 return data; 161 } 162 163 public void setData(byte[] data) { 164 this.data = data; 165 } 166 167 private int calcSize(List<HeaderElement> elements, byte[] data) { 168 int size = 0; 169 if (elements.size() > 0) { 170 // 1 octet for specifying the length of the UDH. 171 size += 1; 172 } 173 if (data != null) { 174 size += data.length; 175 } 176 for (HeaderElement element : elements) { 177 size += element.getLength() + 2; 178 } 179 return size; 180 } 181 182 private List<HeaderElement> dupElements() { 183 return new LinkedList<HeaderElement>(headerElements); 184 } 185}