public BeaconParser setBeaconLayout(String beaconLayout) {
        LogManager.d(TAG, "API setBeaconLayout "+beaconLayout);

        mBeaconLayout = beaconLayout;
        Log.d(TAG, "Parsing beacon layout: "+beaconLayout);

        String[] terms =  beaconLayout.split(",");
        mExtraFrame = false; // this is not an extra frame by default

        for (String term : terms) {
            boolean found = false;

            Matcher matcher = I_PATTERN.matcher(term);
            while (matcher.find()) {
                found = true;
                try {
                    int startOffset = Integer.parseInt(matcher.group(1));
                    int endOffset = Integer.parseInt(matcher.group(2));
                    Boolean littleEndian = matcher.group(3).contains(LITTLE_ENDIAN_SUFFIX);
                    mIdentifierLittleEndianFlags.add(littleEndian);
                    Boolean variableLength = matcher.group(3).contains(VARIABLE_LENGTH_SUFFIX);
                    mIdentifierVariableLengthFlags.add(variableLength);
                    mIdentifierStartOffsets.add(startOffset);
                    mIdentifierEndOffsets.add(endOffset);
                } catch (NumberFormatException e) {
                    throw new BeaconLayoutException("Cannot parse integer byte offset in term: " + term);
                }
            }
            matcher = D_PATTERN.matcher(term);
            while (matcher.find()) {
                found = true;
                try {
                    int startOffset = Integer.parseInt(matcher.group(1));
                    int endOffset = Integer.parseInt(matcher.group(2));
                    Boolean littleEndian = matcher.group(3).contains("l");
                    mDataLittleEndianFlags.add(littleEndian);
                    mDataStartOffsets.add(startOffset);
                    mDataEndOffsets.add(endOffset);
                } catch (NumberFormatException e) {
                    throw new BeaconLayoutException("Cannot parse integer byte offset in term: " + term);
                }
            }
            matcher = P_PATTERN.matcher(term);

            while (matcher.find()) {
                found = true;
                String correctionString = "none";
                try {
                    if (matcher.group(1) != null && matcher.group(2) != null) {
                        int startOffset = Integer.parseInt(matcher.group(1));
                        int endOffset = Integer.parseInt(matcher.group(2));
                        mPowerStartOffset=startOffset;
                        mPowerEndOffset=endOffset;
                    }

                    int dBmCorrection = 0;
                    if (matcher.group(3) != null) {
                        correctionString = matcher.group(3);
                        dBmCorrection = Integer.parseInt(correctionString);
                    }
                    mDBmCorrection=dBmCorrection;
                } catch (NumberFormatException e) {
                    throw new BeaconLayoutException("Cannot parse integer power byte offset ("+correctionString+") in term: " + term);
                }
            }
            matcher = M_PATTERN.matcher(term);
            while (matcher.find()) {
                found = true;
                try {
                    int startOffset = Integer.parseInt(matcher.group(1));
                    int endOffset = Integer.parseInt(matcher.group(2));
                    mMatchingBeaconTypeCodeStartOffset = startOffset;
                    mMatchingBeaconTypeCodeEndOffset = endOffset;
                } catch (NumberFormatException e) {
                    throw new BeaconLayoutException("Cannot parse integer byte offset in term: " + term);
                }
                String hexString = matcher.group(3);
                try {
                    mMatchingBeaconTypeCode = Long.decode("0x"+hexString);
                }
                catch (NumberFormatException e) {
                    throw new BeaconLayoutException("Cannot parse beacon type code: "+hexString+" in term: " + term);
                }
            }
            matcher = S_PATTERN.matcher(term);
            while (matcher.find()) {
                found = true;
                try {
                    int startOffset = Integer.parseInt(matcher.group(1));
                    int endOffset = Integer.parseInt(matcher.group(2));
                    mServiceUuidStartOffset = startOffset;
                    mServiceUuidEndOffset = endOffset;
                } catch (NumberFormatException e) {
                    throw new BeaconLayoutException("Cannot parse integer byte offset in term: " + term);
                }
                String hexString = matcher.group(3);
                if (mServiceUuidEndOffset - mServiceUuidStartOffset + 1 == 2) {
                    try {
                        mServiceUuid = Long.decode("0x" + hexString);
                    } catch (NumberFormatException e) {
                        throw new BeaconLayoutException("Cannot parse serviceUuid: " + hexString + " in term: " + term);
                    }
                } else if (mServiceUuidEndOffset - mServiceUuidStartOffset + 1 == 16) {
                    String bytesString = hexString.replace("-", "");
                    if (bytesString.length() != 32) {
                        throw new BeaconLayoutException("128-bit ServiceUuid must be 16 bytes long: "+hexString+" in term: " + term);
                    }
                    mServiceUuid128Bit = new byte[16];
                    for (int i = 0; i < 16; i++) {
                        String byteString = bytesString.substring(i*2,i*2+2);
                        try {
                            mServiceUuid128Bit[15-i] = (byte) Integer.parseInt(byteString, 16);
                        }
                        catch (NumberFormatException e) {
                            throw new BeaconLayoutException("Cannot parse serviceUuid byte "+byteString+" in term: " + term);
                        }
                    }
                }
                else {
                    throw new BeaconLayoutException("Cannot parse serviceUuid -- it must be 2 bytes or 16 bytes long: " + hexString + " in term: " + term);
                }
            }
            matcher = X_PATTERN.matcher(term);
            while (matcher.find()) {
                found = true;
                mExtraFrame = true;
            }

            if (!found) {
                LogManager.d(TAG, "cannot parse term %s", term);
                throw new BeaconLayoutException("Cannot parse beacon layout term: " + term);
            }
        }
        mLayoutSize = calculateLayoutSize();
        return this;
    }
@Test
    public void testSetBeaconLayout() {
        byte[] bytes = hexStringToByteArray("02011a1bffbeac2f234454cf6d4a0fadf2f4911ba9ffa600010002c509000000");
        BeaconParser parser = new BeaconParser();
        parser.setBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25");

        assertEquals("parser should get beacon type code start offset", new Integer(2), parser.mMatchingBeaconTypeCodeStartOffset);
        assertEquals("parser should get beacon type code end offset",  new Integer(3), parser.mMatchingBeaconTypeCodeEndOffset);
        assertEquals("parser should get beacon type code", new Long(0xbeac), parser.getMatchingBeaconTypeCode());
        assertEquals("parser should get identifier start offset", new Integer(4), parser.mIdentifierStartOffsets.get(0));
        assertEquals("parser should get identifier end offset", new Integer(19), parser.mIdentifierEndOffsets.get(0));
        assertEquals("parser should get identifier start offset", new Integer(20), parser.mIdentifierStartOffsets.get(1));
        assertEquals("parser should get identifier end offset", new Integer(21), parser.mIdentifierEndOffsets.get(1));
        assertEquals("parser should get identifier start offset", new Integer(22), parser.mIdentifierStartOffsets.get(2));
        assertEquals("parser should get identifier end offset", new Integer(23), parser.mIdentifierEndOffsets.get(2));
        assertEquals("parser should get power start offset", new Integer(24), parser.mPowerStartOffset);
        assertEquals("parser should get power end offset", new Integer(24), parser.mPowerEndOffset);
        assertEquals("parser should get data start offset", new Integer(25), parser.mDataStartOffsets.get(0));
        assertEquals("parser should get data end offset", new Integer(25), parser.mDataEndOffsets.get(0));

    }