PageRenderTime 44ms CodeModel.GetById 21ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 0ms

/protocols/smpp/src/main/java/org/mobicents/protocols/smpp/gsm/UserDataImpl.java

http://mobicents.googlecode.com/
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}