/* * TeleStax, Open Source Cloud Communications * Copyright 2012, Telestax Inc and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.mobicents.protocols.ss7.cap.primitives; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import javolution.xml.XMLFormat; import javolution.xml.stream.XMLStreamException; import org.mobicents.protocols.ss7.cap.api.CAPException; import org.mobicents.protocols.ss7.cap.api.primitives.CalledPartyBCDNumber; import org.mobicents.protocols.ss7.map.api.MAPException; import org.mobicents.protocols.ss7.map.api.MAPParsingComponentException; import org.mobicents.protocols.ss7.map.api.primitives.AddressNature; import org.mobicents.protocols.ss7.map.api.primitives.NumberingPlan; import org.mobicents.protocols.ss7.map.datacoding.GSMCharset; import org.mobicents.protocols.ss7.map.datacoding.GSMCharsetDecoder; import org.mobicents.protocols.ss7.map.datacoding.GSMCharsetDecodingData; import org.mobicents.protocols.ss7.map.datacoding.GSMCharsetEncoder; import org.mobicents.protocols.ss7.map.datacoding.Gsm7EncodingStyle; import org.mobicents.protocols.ss7.map.primitives.TbcdString; /** * * @author sergey vetyutnev * */ public class CalledPartyBCDNumberImpl extends OctetStringBase implements CalledPartyBCDNumber { private static final String NAI = "nai"; private static final String NPI = "npi"; private static final String NUMBER = "number"; // private static final String IS_EXTENSION = "isExtension"; private static final String DEFAULT_STRING_VALUE = null; protected static final int NO_EXTENSION_MASK = 0x80; protected static final int NATURE_OF_ADD_IND_MASK = 0x70; protected static final int NUMBERING_PLAN_IND_MASK = 0x0F; public CalledPartyBCDNumberImpl() { super(1, 41, "CalledPartyBCDNumber"); } public CalledPartyBCDNumberImpl(byte[] data) { super(1, 41, "CalledPartyBCDNumber", data); } public CalledPartyBCDNumberImpl(AddressNature addressNature, NumberingPlan numberingPlan, String address) throws CAPException { super(1, 41, "CalledPartyBCDNumber"); this.setParameters(addressNature, numberingPlan, address); } protected void setParameters(AddressNature addressNature, NumberingPlan numberingPlan, String address) throws CAPException { if (addressNature == null || numberingPlan == null || address == null) throw new CAPException("Error when encoding " + _PrimitiveName + ": addressNature, numberingPlan or address is empty"); this._testLengthEncode(address); ByteArrayOutputStream stm = new ByteArrayOutputStream(); int nature = 0x80; // if (!isExtension) // nature = 0x80; // else // nature = 0; nature = nature | (addressNature.getIndicator() << 4); nature = nature | (numberingPlan.getIndicator()); stm.write(nature); if (numberingPlan == NumberingPlan.spare_5) { // -- In the context of the DestinationSubscriberNumber field in ConnectSMSArg or // -- InitialDPSMSArg, a CalledPartyBCDNumber may also contain an alphanumeric // -- character string. In this case, type-of-number '101'B is used, in accordance // -- with 3GPP TS 23.040 [6]. The address is coded in accordance with the // -- GSM 7-bit default alphabet definition and the SMS packing rules // -- as specified in 3GPP TS 23.038 [15] in this case. GSMCharset cs = new GSMCharset(GSMCharset.GSM_CANONICAL_NAME, new String[] {}); GSMCharsetEncoder encoder = (GSMCharsetEncoder) cs.newEncoder(); ByteBuffer bb; try { bb = encoder.encode(CharBuffer.wrap(address)); int dataLength = bb.limit(); byte[] data = new byte[dataLength]; bb.get(data); stm.write(data); } catch (CharacterCodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { try { TbcdString.encodeString(stm, address); } catch (MAPException e) { throw new CAPException(e); } } this.data = stm.toByteArray(); } public byte[] getData() { return data; } public AddressNature getAddressNature() { if (this.data == null || this.data.length == 0) return null; int nature = this.data[0]; int natureOfAddInd = ((nature & NATURE_OF_ADD_IND_MASK) >> 4); return AddressNature.getInstance(natureOfAddInd); } public NumberingPlan getNumberingPlan() { if (this.data == null || this.data.length == 0) return null; int nature = this.data[0]; int numbPlanInd = (nature & NUMBERING_PLAN_IND_MASK); return NumberingPlan.getInstance(numbPlanInd); } public boolean isExtension() { if (this.data == null || this.data.length == 0) return false; int nature = this.data[0]; if ((nature & NO_EXTENSION_MASK) == 0x80) return false; else return true; } public String getAddress() { if (this.data == null || this.data.length == 0) return null; try { ByteArrayInputStream stm = new ByteArrayInputStream(this.data); stm.read(); if (this.getNumberingPlan() == NumberingPlan.spare_5) { // -- In the context of the DestinationSubscriberNumber field in ConnectSMSArg or // -- InitialDPSMSArg, a CalledPartyBCDNumber may also contain an alphanumeric // -- character string. In this case, type-of-number '101'B is used, in accordance // -- with 3GPP TS 23.040 [6]. The address is coded in accordance with the // -- GSM 7-bit default alphabet definition and the SMS packing rules // -- as specified in 3GPP TS 23.038 [15] in this case. if (data.length == 1) return ""; int addressLength = this.data.length - 1; ByteBuffer bb = ByteBuffer.wrap(this.data, 1, addressLength); GSMCharset cs = new GSMCharset(GSMCharset.GSM_CANONICAL_NAME, new String[] {}); GSMCharsetDecoder decoder = (GSMCharsetDecoder) cs.newDecoder(); int totalSeptetCount = addressLength + (addressLength / 8); GSMCharsetDecodingData encodingData = new GSMCharsetDecodingData(Gsm7EncodingStyle.bit7_sms_style, totalSeptetCount, 0); decoder.setGSMCharsetDecodingData(encodingData); CharBuffer bf = decoder.decode(bb); return bf.toString(); } else { String address = TbcdString.decodeString(stm, this.data.length - 1); return address; } } catch (MAPParsingComponentException e) { return null; } catch (IOException e) { return null; } } protected void _testLengthEncode(String address) throws CAPException { if (address.length() > 38) throw new CAPException("Error when encoding AddressString: address length must not exceed 38 digits"); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(_PrimitiveName); sb.append(" ["); if (this.getAddressNature() != null) { sb.append("addressNature="); sb.append(this.getAddressNature()); } if (this.getNumberingPlan() != null) { sb.append(", numberingPlan="); sb.append(this.getNumberingPlan()); } if (this.getAddress() != null) { sb.append(", address="); sb.append(this.getAddress()); } if (this.isExtension()) { sb.append(", extension"); } sb.append("]"); return sb.toString(); } /** * XML Serialization/Deserialization */ protected static final XMLFormat<CalledPartyBCDNumberImpl> CALLED_PARTY_BCD_NUMBER_XML = new XMLFormat<CalledPartyBCDNumberImpl>( CalledPartyBCDNumberImpl.class) { @Override public void read(javolution.xml.XMLFormat.InputElement xml, CalledPartyBCDNumberImpl calledPartyBCDNumber) throws XMLStreamException { try { AddressNature addressNature = null; NumberingPlan numberingPlan = null; String nai = xml.getAttribute(NAI, DEFAULT_STRING_VALUE); String npi = xml.getAttribute(NPI, DEFAULT_STRING_VALUE); if (nai != null) { addressNature = Enum.valueOf(AddressNature.class, nai); } if (npi != null) { numberingPlan = Enum.valueOf(NumberingPlan.class, npi); } calledPartyBCDNumber.setParameters(addressNature, numberingPlan, xml.getAttribute(NUMBER, "")); } catch (CAPException e) { throw new XMLStreamException("CAPException when CalledPartyBCDNumber data setting", e); } } @Override public void write(CalledPartyBCDNumberImpl calledPartyBCDNumber, javolution.xml.XMLFormat.OutputElement xml) throws XMLStreamException { xml.setAttribute(NUMBER, calledPartyBCDNumber.getAddress()); xml.setAttribute(NAI, calledPartyBCDNumber.getAddressNature().toString()); xml.setAttribute(NPI, calledPartyBCDNumber.getNumberingPlan().toString()); // if (calledPartyBCDNumber.isExtension()) { // xml.setAttribute(IS_EXTENSION, true); // } } }; }