Merge branch 'clh' into liuyang
# Conflicts:
# SourceCode/Bond/Servo/Servo.rc
# SourceCode/Bond/Servo/Servo.vcxproj
# SourceCode/Bond/Servo/Servo.vcxproj.filters
# SourceCode/Bond/Servo/resource.h
| | |
| | | |
| | | CGlass::~CGlass() |
| | | { |
| | | |
| | | reset(); |
| | | } |
| | | |
| | | void CGlass::reset() |
| | |
| | | public: |
| | | virtual std::string& getClassName(); |
| | | virtual std::string toString(); |
| | | short getCassetteSequenceNo() { return m_jobDataS.getCassetteSequenceNo(); } |
| | | short getJobSequenceNo() { return m_jobDataS.getJobSequenceNo(); } |
| | | MaterialsType getType(); |
| | | void setType(MaterialsType type); |
| | | void setID(const char* pszID); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | // CPageGlassList.cpp: å®ç°æä»¶ |
| | | // |
| | | |
| | | #include "stdafx.h" |
| | | #include "Servo.h" |
| | | #include "CPageGlassList.h" |
| | | #include "afxdialogex.h" |
| | | #include "GlassJson.h" |
| | | |
| | | |
| | | #define PAGE_SIZE 100 |
| | | #define PAGE_BACKGROUND_COLOR RGB(252, 252, 255) |
| | | |
| | | // CPageGlassList å¯¹è¯æ¡ |
| | | |
| | | IMPLEMENT_DYNAMIC(CPageGlassList, CDialogEx) |
| | | |
| | | CPageGlassList::CPageGlassList(CWnd* pParent /*=nullptr*/) |
| | | : CDialogEx(IDD_PAGE_GLASS_LIST, pParent) |
| | | { |
| | | m_crBkgnd = PAGE_BACKGROUND_COLOR; |
| | | m_hbrBkgnd = nullptr; |
| | | m_pObserver = nullptr; |
| | | |
| | | m_strStatus = ""; |
| | | m_strKeyword = ""; |
| | | m_nCurPage = 0; |
| | | m_nTotalPages = 0; |
| | | |
| | | memset(m_szTimeStart, 0, sizeof(m_szTimeStart)); |
| | | memset(m_szTimeEnd, 0, sizeof(m_szTimeEnd)); |
| | | m_szTimeStart[0] = '\0'; |
| | | m_szTimeEnd[0] = '\0'; |
| | | } |
| | | |
| | | CPageGlassList::~CPageGlassList() |
| | | { |
| | | if (m_hbrBkgnd != nullptr) { |
| | | ::DeleteObject(m_hbrBkgnd); |
| | | } |
| | | if (m_pObserver != nullptr) { |
| | | m_pObserver->unsubscribe(); |
| | | m_pObserver = nullptr; |
| | | } |
| | | } |
| | | |
| | | void CPageGlassList::DoDataExchange(CDataExchange* pDX) |
| | | { |
| | | CDialogEx::DoDataExchange(pDX); |
| | | DDX_Control(pDX, IDC_DATETIMEPICKER_START, m_dateTimeStart); |
| | | DDX_Control(pDX, IDC_DATETIMEPICKER_END, m_dateTimeEnd); |
| | | DDX_Control(pDX, IDC_LIST_ALARM, m_listCtrl); |
| | | } |
| | | |
| | | |
| | | BEGIN_MESSAGE_MAP(CPageGlassList, CDialogEx) |
| | | ON_WM_CTLCOLOR() |
| | | ON_WM_DESTROY() |
| | | ON_WM_SIZE() |
| | | ON_WM_TIMER() |
| | | ON_CBN_SELCHANGE(IDC_COMBO_DATETIME, &CPageGlassList::OnCbnSelchangeComboDatetime) |
| | | ON_CBN_SELCHANGE(IDC_COMBO_STATUS_FILTER, &CPageGlassList::OnCbnSelchangeComboStatusFilter) |
| | | ON_BN_CLICKED(IDC_BUTTON_SEARCH, &CPageGlassList::OnBnClickedButtonSearch) |
| | | ON_BN_CLICKED(IDC_BUTTON_EXPORT, &CPageGlassList::OnBnClickedButtonExport) |
| | | ON_BN_CLICKED(IDC_BUTTON_PREV_PAGE, &CPageGlassList::OnBnClickedButtonPrevPage) |
| | | ON_BN_CLICKED(IDC_BUTTON_NEXT_PAGE, &CPageGlassList::OnBnClickedButtonNextPage) |
| | | END_MESSAGE_MAP() |
| | | |
| | | |
| | | // CPageGlassList æ¶æ¯å¤çç¨åº |
| | | void CPageGlassList::InitRxWindow() |
| | | { |
| | | /* code */ |
| | | // 订é
æ°æ® |
| | | IRxWindows* pRxWindows = RX_GetRxWindows(); |
| | | pRxWindows->enableLog(5); |
| | | if (m_pObserver == NULL) { |
| | | m_pObserver = pRxWindows->allocObserver([&](IAny* pAny) -> void { |
| | | // onNext |
| | | pAny->addRef(); |
| | | int code = pAny->getCode(); |
| | | |
| | | if (RX_CODE_EQ_ROBOT_TASK == code) { |
| | | UpdatePageData(); |
| | | } |
| | | |
| | | pAny->release(); |
| | | }, [&]() -> void { |
| | | // onComplete |
| | | }, [&](IThrowable* pThrowable) -> void { |
| | | // onErrorm |
| | | pThrowable->printf(); |
| | | }); |
| | | |
| | | theApp.m_model.getObservable()->observeOn(pRxWindows->mainThread())->subscribe(m_pObserver); |
| | | } |
| | | } |
| | | |
| | | void CPageGlassList::Resize() |
| | | { |
| | | CRect rcClient; |
| | | GetClientRect(&rcClient); |
| | | |
| | | // ===== 常éå®ä¹ ===== |
| | | const int nLeft = 12; |
| | | const int nRight = 12; |
| | | const int nTop = 58; |
| | | const int nButtonHeight = 28; |
| | | const int nButtonMarginBottom = 12; |
| | | const int nSpacing = 8; |
| | | const int nButtonWidth = 80; |
| | | const int nLabelWidth = 100; |
| | | |
| | | // ===== å页æ§ä»¶å¸å± ===== |
| | | int yBottom = rcClient.bottom - nButtonMarginBottom - nButtonHeight; |
| | | int xRight = rcClient.Width() - nRight; |
| | | |
| | | CWnd* pBtnNext = GetDlgItem(IDC_BUTTON_NEXT_PAGE); |
| | | CWnd* pBtnPrev = GetDlgItem(IDC_BUTTON_PREV_PAGE); |
| | | CWnd* pLabelPage = GetDlgItem(IDC_LABEL_PAGE_NUMBER); |
| | | |
| | | if (pBtnNext && pBtnPrev && pLabelPage) { |
| | | // è·ååé¡µææ¬å®½åº¦ä¼°ç® |
| | | //CString strLabel; |
| | | //GetDlgItemText(IDC_LABEL_PAGE_NUMBER, strLabel); |
| | | //if (strLabel.IsEmpty()) { |
| | | // strLabel = _T("第 1 / 1 页"); |
| | | //} |
| | | //int nCharWidth = 8; |
| | | //int nLabelWidth = strLabel.GetLength() * nCharWidth + 20; |
| | | |
| | | // 设置æé®åæ ç¾ä½ç½® |
| | | pBtnNext->MoveWindow(xRight - nButtonWidth, yBottom, nButtonWidth, nButtonHeight); |
| | | xRight -= nButtonWidth + nSpacing; |
| | | |
| | | pLabelPage->MoveWindow(xRight - nLabelWidth, yBottom, nLabelWidth, nButtonHeight); |
| | | xRight -= nLabelWidth + nSpacing; |
| | | |
| | | pBtnPrev->MoveWindow(xRight - nButtonWidth, yBottom, nButtonWidth, nButtonHeight); |
| | | } |
| | | |
| | | // ===== è¡¨æ ¼åºåå¸å± ===== |
| | | if (nullptr != m_listCtrl.m_hWnd) { |
| | | int listHeight = yBottom - nTop - nSpacing; |
| | | m_listCtrl.MoveWindow(nLeft, nTop, rcClient.Width() - nLeft - nRight, listHeight); |
| | | } |
| | | } |
| | | |
| | | void CPageGlassList::InitStatusCombo() |
| | | { |
| | | CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_STATUS_FILTER); |
| | | if (nullptr != pComboBox) { |
| | | pComboBox->ResetContent(); |
| | | pComboBox->AddString(_T("å
¨é¨")); |
| | | pComboBox->AddString(_T("Ready")); |
| | | pComboBox->AddString(_T("Running")); |
| | | pComboBox->AddString(_T("Error")); |
| | | pComboBox->AddString(_T("Abort")); |
| | | pComboBox->AddString(_T("Completed")); |
| | | pComboBox->SetCurSel(0); |
| | | } |
| | | } |
| | | |
| | | void CPageGlassList::InitTimeRangeCombo() |
| | | { |
| | | CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_DATETIME); |
| | | if (nullptr != pComboBox) { |
| | | pComboBox->ResetContent(); |
| | | pComboBox->AddString(_T("ä¸é")); |
| | | pComboBox->AddString(_T("ä»å¤©")); |
| | | pComboBox->AddString(_T("ä¸å¤©å
")); |
| | | pComboBox->AddString(_T("æ¬æ")); |
| | | pComboBox->AddString(_T("ä»å¹´")); |
| | | pComboBox->AddString(_T("èªå®ä¹")); |
| | | pComboBox->SetCurSel(0); |
| | | } |
| | | } |
| | | |
| | | void CPageGlassList::InitDateTimeControls() |
| | | { |
| | | if (m_dateTimeStart.m_hWnd == nullptr || m_dateTimeEnd.m_hWnd == nullptr) { |
| | | return; |
| | | } |
| | | |
| | | // ç¦ç¨åå§ç¶æ |
| | | m_dateTimeStart.EnableWindow(FALSE); |
| | | m_dateTimeEnd.EnableWindow(FALSE); |
| | | |
| | | // è®¾ç½®æ ¼å¼ï¼æ¾ç¤ºæ¥æ + æ¶é´ |
| | | //m_dateTimeStart.SetFormat(_T("yyyy/MM/dd HH:mm:ss")); |
| | | //m_dateTimeEnd.SetFormat(_T("yyyy/MM/dd HH:mm:ss")); |
| | | |
| | | // ä¿®æ¹æ ·å¼ä»¥æ¯ææ¶é´æ ¼å¼ |
| | | //DWORD dwStyleStart = m_dateTimeStart.GetStyle(); |
| | | //DWORD dwStyleEnd = m_dateTimeEnd.GetStyle(); |
| | | |
| | | //m_dateTimeStart.ModifyStyle(0, DTS_TIMEFORMAT | DTS_UPDOWN); |
| | | //m_dateTimeEnd.ModifyStyle(0, DTS_TIMEFORMAT); |
| | | } |
| | | |
| | | void CPageGlassList::LoadTransfers() |
| | | { |
| | | m_nCurPage = 1; |
| | | UpdatePageData(); |
| | | } |
| | | |
| | | void CPageGlassList::UpdatePageData() |
| | | { |
| | | /* |
| | | TransferData filter; |
| | | filter.strStatus = m_strStatus; |
| | | filter.strDescription = m_strKeyword; |
| | | filter.strCreateTime = m_szTimeStart; |
| | | filter.strEndTime = m_szTimeEnd; |
| | | auto vecData = TransferManager::getInstance().getTransfers(filter, m_nCurPage, PAGE_SIZE); |
| | | FillDataToListCtrl(vecData); |
| | | |
| | | int nTotalRecords = TransferManager::getInstance().getFilteredTransferCount(filter); |
| | | m_nTotalPages = (nTotalRecords + PAGE_SIZE - 1) / PAGE_SIZE; |
| | | UpdatePageControls(); |
| | | */ |
| | | } |
| | | |
| | | void CPageGlassList::UpdatePageControls() |
| | | { |
| | | CString strPage; |
| | | strPage.Format(_T("第 %d / %d 页"), m_nCurPage, m_nTotalPages); |
| | | SetDlgItemText(IDC_LABEL_PAGE_NUMBER, strPage); |
| | | GetDlgItem(IDC_BUTTON_PREV_PAGE)->EnableWindow(m_nCurPage > 1); |
| | | GetDlgItem(IDC_BUTTON_NEXT_PAGE)->EnableWindow(m_nCurPage < m_nTotalPages); |
| | | |
| | | Resize(); |
| | | } |
| | | |
| | | void CPageGlassList::UpdateDateFilter() |
| | | { |
| | | CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_DATETIME); |
| | | if (nullptr != pComboBox) { |
| | | int nIndex = pComboBox->GetCurSel(); |
| | | if (nIndex == 0) { |
| | | memset(m_szTimeStart, 0, sizeof(m_szTimeStart)); |
| | | memset(m_szTimeEnd, 0, sizeof(m_szTimeEnd)); |
| | | m_szTimeStart[0] = '\0'; |
| | | m_szTimeEnd[0] = '\0'; |
| | | } |
| | | else { |
| | | CTime time = CTime::GetCurrentTime(); |
| | | if (nIndex == 1) { |
| | | sprintf_s(m_szTimeStart, 64, "%d-%02d-%02d 00:00:00", time.GetYear(), time.GetMonth(), time.GetDay()); |
| | | sprintf_s(m_szTimeEnd, 64, "%d-%02d-%02d 23:59:59", time.GetYear(), time.GetMonth(), time.GetDay()); |
| | | } |
| | | else if (nIndex == 2) { |
| | | CTime time2 = time - CTimeSpan(7, 0, 0, 0); |
| | | sprintf_s(m_szTimeStart, 64, "%d-%02d-%02d 00:00:00", time2.GetYear(), time2.GetMonth(), time2.GetDay()); |
| | | sprintf_s(m_szTimeEnd, 64, "%d-%02d-%02d 23:59:59", time.GetYear(), time.GetMonth(), time.GetDay()); |
| | | } |
| | | else if (nIndex == 3) { |
| | | sprintf_s(m_szTimeStart, 64, "%d-%02d-01 00:00:00", time.GetYear(), time.GetMonth()); |
| | | sprintf_s(m_szTimeEnd, 64, "%d-%02d-%02d 23:59:59", time.GetYear(), time.GetMonth(), time.GetDay()); |
| | | } |
| | | else if (nIndex == 4) { |
| | | sprintf_s(m_szTimeStart, 64, "%d-01-01 00:00:00", time.GetYear()); |
| | | sprintf_s(m_szTimeEnd, 64, "%d-12-31 23:59:59", time.GetYear()); |
| | | } |
| | | else if (nIndex == 5) { |
| | | SYSTEMTIME t1, t2; |
| | | m_dateTimeStart.GetTime(&t1); |
| | | m_dateTimeEnd.GetTime(&t2); |
| | | //sprintf_s(m_szTimeStart, 64, "%d-%02d-%02d %02d:%02d:%02d", t1.wYear, t1.wMonth, t1.wDay, t1.wHour, t1.wMinute, t1.wSecond); |
| | | //sprintf_s(m_szTimeEnd, 64, "%d-%02d-%02d %02d:%02d:%02d", t2.wYear, t2.wMonth, t2.wDay, t2.wHour, t2.wMinute, t2.wSecond); |
| | | |
| | | sprintf_s(m_szTimeStart, 64, "%d-%02d-%02d 00:00:00", t1.wYear, t1.wMonth, t1.wDay); |
| | | sprintf_s(m_szTimeEnd, 64, "%d-%02d-%02d 23:59:59", t2.wYear, t2.wMonth, t2.wDay); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /* |
| | | void CPageGlassList::FillDataToListCtrl(const std::vector<TransferData>& vecData) |
| | | { |
| | | if (m_listCtrl.m_hWnd == nullptr) { |
| | | return; |
| | | } |
| | | |
| | | m_listCtrl.DeleteAllItems(); |
| | | for (const auto& item : vecData) { |
| | | InsertTransferData(item); |
| | | } |
| | | } |
| | | |
| | | void CPageGlassList::InsertTransferData(const TransferData& data) |
| | | { |
| | | if (m_listCtrl.m_hWnd == nullptr) { |
| | | return; |
| | | } |
| | | |
| | | int nIndex = m_listCtrl.GetItemCount(); |
| | | if (nIndex < 0) { |
| | | return; |
| | | } |
| | | |
| | | int nItem = m_listCtrl.InsertItem(nIndex, _T("")); |
| | | CString str; |
| | | str.Format(_T("%d"), data.nRecordId); |
| | | m_listCtrl.SetItemText(nItem, 1, str); |
| | | m_listCtrl.SetItemText(nItem, 2, CString(data.strStatus.c_str())); |
| | | m_listCtrl.SetItemText(nItem, 3, CString(data.strClassID.c_str())); |
| | | m_listCtrl.SetItemText(nItem, 4, CString(data.strCreateTime.c_str())); |
| | | m_listCtrl.SetItemText(nItem, 5, CString(data.strPickTime.c_str())); |
| | | m_listCtrl.SetItemText(nItem, 6, CString(data.strPlaceTime.c_str())); |
| | | m_listCtrl.SetItemText(nItem, 7, CString(data.strEndTime.c_str())); |
| | | m_listCtrl.SetItemText(nItem, 8, CString(data.strDescription.c_str())); |
| | | } |
| | | */ |
| | | // CPageTransferLog æ¶æ¯å¤çç¨åº |
| | | |
| | | BOOL CPageGlassList::OnInitDialog() |
| | | { |
| | | CDialogEx::OnInitDialog(); |
| | | |
| | | // TODO: 卿¤æ·»å é¢å¤çåå§å |
| | | SetTimer(1, 3000, nullptr); |
| | | |
| | | // ä¸ææ¡æ§ä»¶ |
| | | InitStatusCombo(); |
| | | InitTimeRangeCombo(); |
| | | |
| | | // æ¥ææ§ä»¶ |
| | | InitDateTimeControls(); |
| | | |
| | | // æ¥è¡¨æ§ä»¶ |
| | | CString strIniFile, strItem; |
| | | strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir); |
| | | |
| | | DWORD dwStyle = m_listCtrl.GetExtendedStyle(); |
| | | dwStyle |= LVS_EX_FULLROWSELECT; |
| | | dwStyle |= LVS_EX_GRIDLINES; |
| | | m_listCtrl.SetExtendedStyle(dwStyle); |
| | | |
| | | HIMAGELIST imageList = ImageList_Create(24, 24, ILC_COLOR24, 1, 1); |
| | | ListView_SetImageList(m_listCtrl.GetSafeHwnd(), imageList, LVSIL_SMALL); |
| | | |
| | | CString headers[] = { |
| | | _T(""), |
| | | _T("Cassette Sequence No"), |
| | | _T("Job Sequence No"), |
| | | _T("Class ID"), |
| | | _T("ç©æç±»å"), |
| | | _T("ç¶æ"), |
| | | _T("å·¥èºå¼å§æ¶é´"), |
| | | _T("å·¥èºç»ææ¶é´"), |
| | | _T("é¦å®Glass ID"), |
| | | _T("AOIæ£æµç»æ"), |
| | | _T("è·¯å¾"), |
| | | _T("å·¥èºåæ°") |
| | | }; |
| | | int widths[] = { 0, 80, 80, 100, 120, 120, 120, 120, 200, 200, 200, 200 }; |
| | | for (int i = 0; i < _countof(headers); ++i) { |
| | | strItem.Format(_T("Col_%d_Width"), i); |
| | | widths[i] = GetPrivateProfileInt("GlassListCtrl", strItem, widths[i], strIniFile); |
| | | m_listCtrl.InsertColumn(i, headers[i], LVCFMT_LEFT, widths[i]); |
| | | } |
| | | m_listCtrl.SetColumnWidth(10, LVSCW_AUTOSIZE_USEHEADER); |
| | | |
| | | |
| | | // è®¡ç®æ»é¡µæ° |
| | | /* |
| | | int nTotalRecords = TransferManager::getInstance().getTotalTransferCountAll(); |
| | | m_nTotalPages = (nTotalRecords + PAGE_SIZE - 1) / PAGE_SIZE; |
| | | m_nCurPage = 1; |
| | | */ |
| | | |
| | | Resize(); |
| | | LoadTransfers(); |
| | | |
| | | return TRUE; // return TRUE unless you set the focus to a control |
| | | // å¼å¸¸: OCX 屿§é¡µåºè¿å FALSE |
| | | } |
| | | |
| | | HBRUSH CPageGlassList::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) |
| | | { |
| | | if (nCtlColor == CTLCOLOR_STATIC) { |
| | | pDC->SetBkColor(m_crBkgnd); |
| | | } |
| | | |
| | | if (m_hbrBkgnd == nullptr) { |
| | | m_hbrBkgnd = CreateSolidBrush(m_crBkgnd); |
| | | } |
| | | |
| | | return m_hbrBkgnd; |
| | | } |
| | | |
| | | void CPageGlassList::OnDestroy() |
| | | { |
| | | CDialogEx::OnDestroy(); |
| | | if (m_hbrBkgnd != nullptr) { |
| | | ::DeleteObject(m_hbrBkgnd); |
| | | m_hbrBkgnd = nullptr; |
| | | } |
| | | if (m_pObserver != nullptr) { |
| | | m_pObserver->unsubscribe(); |
| | | m_pObserver = nullptr; |
| | | } |
| | | |
| | | // ä¿åå宽 |
| | | CString strIniFile, strItem, strTemp; |
| | | strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir); |
| | | CHeaderCtrl* pHeader = m_listCtrl.GetHeaderCtrl(); |
| | | for (int i = 0; i < pHeader->GetItemCount(); i++) { |
| | | RECT rect; |
| | | pHeader->GetItemRect(i, &rect); |
| | | strItem.Format(_T("Col_%d_Width"), i); |
| | | strTemp.Format(_T("%d"), rect.right - rect.left); |
| | | WritePrivateProfileString("GlassListCtrl", strItem, strTemp, strIniFile); |
| | | } |
| | | } |
| | | |
| | | void CPageGlassList::OnSize(UINT nType, int cx, int cy) |
| | | { |
| | | CDialogEx::OnSize(nType, cx, cy); |
| | | Resize(); |
| | | } |
| | | |
| | | void CPageGlassList::OnTimer(UINT_PTR nIDEvent) |
| | | { |
| | | if (nIDEvent == 1) { |
| | | KillTimer(1); |
| | | InitRxWindow(); |
| | | } |
| | | CDialogEx::OnTimer(nIDEvent); |
| | | } |
| | | |
| | | void CPageGlassList::OnCbnSelchangeComboDatetime() |
| | | { |
| | | CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_DATETIME); |
| | | int nIndex = pComboBox->GetCurSel(); |
| | | int nCount = pComboBox->GetCount(); |
| | | m_dateTimeStart.EnableWindow(nIndex == nCount - 1); |
| | | m_dateTimeEnd.EnableWindow(nIndex == nCount - 1); |
| | | |
| | | // æ´æ°æ¥æè¿æ»¤å¨å页颿°æ® |
| | | UpdateDateFilter(); |
| | | LoadTransfers(); |
| | | } |
| | | |
| | | void CPageGlassList::OnCbnSelchangeComboStatusFilter() |
| | | { |
| | | CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_STATUS_FILTER); |
| | | int nIndex = pComboBox->GetCurSel(); |
| | | if (nIndex == 0) { |
| | | m_strStatus.clear(); |
| | | } |
| | | else { |
| | | CString cstrText; |
| | | pComboBox->GetLBText(nIndex, cstrText); |
| | | m_strStatus = CT2A(cstrText); |
| | | } |
| | | LoadTransfers(); |
| | | } |
| | | |
| | | void CPageGlassList::OnBnClickedButtonSearch() |
| | | { |
| | | // è·åå
³é®åè¾å
¥æ¡å
容 |
| | | CString strKeyword; |
| | | GetDlgItemText(IDC_EDIT_KEYWORD, strKeyword); |
| | | m_strKeyword = CT2A(strKeyword); |
| | | |
| | | // æ´æ°æ¥æè¿æ»¤å¨å页颿°æ® |
| | | UpdateDateFilter(); |
| | | LoadTransfers(); |
| | | } |
| | | |
| | | void CPageGlassList::OnBnClickedButtonExport() |
| | | { |
| | | CFileDialog fileDialog(FALSE, _T("csv"), NULL, OFN_HIDEREADONLY, _T("CSV Files (*.csv)|*.csv||")); |
| | | if (fileDialog.DoModal() != IDOK) { |
| | | return; |
| | | } |
| | | |
| | | CStdioFile file; |
| | | if (!file.Open(fileDialog.GetPathName(), CFile::modeCreate | CFile::modeWrite | CFile::typeText)) { |
| | | AfxMessageBox(_T("å建æä»¶å¤±è´¥ï¼")); |
| | | return; |
| | | } |
| | | |
| | | CString strHeader = _T("ä»»å¡ID,ç¶æ,ClassID,å建æ¶é´,åçæ¶é´,æ¾çæ¶é´,ç»ææ¶é´,æè¿°\n"); |
| | | file.WriteString(strHeader); |
| | | |
| | | for (int i = 0; i < m_listCtrl.GetItemCount(); ++i) { |
| | | CString row; |
| | | for (int j = 1; j <= 8; ++j) { |
| | | row += m_listCtrl.GetItemText(i, j); |
| | | if (j != 8) { |
| | | row += ","; |
| | | } |
| | | } |
| | | row += "\n"; |
| | | file.WriteString(row); |
| | | } |
| | | file.Close(); |
| | | } |
| | | |
| | | void CPageGlassList::OnBnClickedButtonPrevPage() |
| | | { |
| | | SERVO::CGlass g; |
| | | g.setID("GLS-001"); |
| | | g.setType(SERVO::MaterialsType::G1); |
| | | g.setOriginPort(1, 5); |
| | | g.setScheduledForProcessing(TRUE); |
| | | g.m_failReason = "none"; |
| | | g.markQueued(); |
| | | g.markStart(); |
| | | |
| | | |
| | | // æ·»å åæ° |
| | | CParam p1("æ ¡æ£å¯¹ä½å»¶æ¶", "P001", "ms", 123); |
| | | CParam p2("温度", "P002", "degC", 25.5); |
| | | g.getParams().push_back(p1); |
| | | g.getParams().push_back(p2); |
| | | |
| | | // 设置 JobDataS |
| | | SERVO::CJobDataS* js = g.getJobDataS(); |
| | | js->setCassetteSequenceNo(10); |
| | | js->setJobSequenceNo(20); |
| | | js->setLotId("LOT-ABC"); |
| | | js->setGlass1Id("GLS-001"); |
| | | |
| | | // æ·»å Path |
| | | g.addPath(100, 1); |
| | | SERVO::CPath* tail = g.getPath()->getTailPath(); |
| | | tail->setInTime(111111); |
| | | tail->setOutTime(222222); |
| | | tail->setInspResult(SERVO::InspResult::Pass); |
| | | tail->processEnd(); |
| | | return; |
| | | // 2. 转为 JSON |
| | | std::string jsonText = GlassJson::ToPrettyString(g); |
| | | TRACE("åºååç»æ:\n%s\n\n", jsonText.c_str()); |
| | | |
| | | // 3. ååºåå |
| | | SERVO::CGlass g2; |
| | | std::string err; |
| | | if (!GlassJson::FromString(jsonText, g2, &err)) { |
| | | TRACE("è§£æå¤±è´¥: %s\n", err.c_str()); |
| | | return; |
| | | } |
| | | |
| | | // 4. æå°éªè¯ |
| | | TRACE("ååºåååçID: %s\n", g2.getID().c_str()); |
| | | TRACE("ååºåååçåæ°æ°é: %d\n", (int)g2.getParams().size()); |
| | | if (!g2.getParams().empty()) { |
| | | TRACE("第ä¸ä¸ªåæ°å: %s å¼=%d\n", |
| | | g2.getParams()[0].getName().c_str(), |
| | | g2.getParams()[0].getIntValue()); |
| | | } |
| | | |
| | | |
| | | |
| | | if (m_nCurPage > 1) { |
| | | m_nCurPage--; |
| | | UpdatePageData(); |
| | | } |
| | | |
| | | } |
| | | |
| | | void CPageGlassList::OnBnClickedButtonNextPage() |
| | | { |
| | | if (m_nCurPage < m_nTotalPages) { |
| | | m_nCurPage++; |
| | | UpdatePageData(); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #pragma once |
| | | #include "ListCtrlEx.h" |
| | | |
| | | |
| | | // CPageGlassList å¯¹è¯æ¡ |
| | | |
| | | class CPageGlassList : public CDialogEx |
| | | { |
| | | DECLARE_DYNAMIC(CPageGlassList) |
| | | |
| | | public: |
| | | CPageGlassList(CWnd* pParent = nullptr); // æ åæé 彿° |
| | | virtual ~CPageGlassList(); |
| | | |
| | | private: |
| | | COLORREF m_crBkgnd; |
| | | HBRUSH m_hbrBkgnd; |
| | | IObserver* m_pObserver; |
| | | |
| | | // æç´¢å
³é®å |
| | | std::string m_strStatus; |
| | | std::string m_strKeyword; |
| | | |
| | | // 页ç |
| | | int m_nCurPage; |
| | | int m_nTotalPages; |
| | | |
| | | // æ¥æ |
| | | char m_szTimeStart[64]; |
| | | char m_szTimeEnd[64]; |
| | | |
| | | // æ§ä»¶ |
| | | CDateTimeCtrl m_dateTimeStart; |
| | | CDateTimeCtrl m_dateTimeEnd; |
| | | CListCtrlEx m_listCtrl; |
| | | |
| | | private: |
| | | void InitRxWindow(); |
| | | void Resize(); |
| | | void InitStatusCombo(); |
| | | void InitTimeRangeCombo(); |
| | | void InitDateTimeControls(); |
| | | void LoadTransfers(); |
| | | void UpdatePageData(); |
| | | void UpdatePageControls(); |
| | | void UpdateDateFilter(); |
| | | /* |
| | | void FillDataToListCtrl(const std::vector<TransferData>& vecData); |
| | | void InsertTransferData(const TransferData& data); |
| | | */ |
| | | |
| | | // å¯¹è¯æ¡æ°æ® |
| | | #ifdef AFX_DESIGN_TIME |
| | | enum { IDD = IDD_PAGE_GLASS_LIST }; |
| | | #endif |
| | | |
| | | protected: |
| | | virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV æ¯æ |
| | | virtual BOOL OnInitDialog(); |
| | | afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); |
| | | afx_msg void OnDestroy(); |
| | | afx_msg void OnSize(UINT nType, int cx, int cy); |
| | | afx_msg void OnTimer(UINT_PTR nIDEvent); |
| | | afx_msg void OnCbnSelchangeComboDatetime(); |
| | | afx_msg void OnCbnSelchangeComboStatusFilter(); |
| | | afx_msg void OnBnClickedButtonSearch(); |
| | | afx_msg void OnBnClickedButtonExport(); |
| | | afx_msg void OnBnClickedButtonPrevPage(); |
| | | afx_msg void OnBnClickedButtonNextPage(); |
| | | DECLARE_MESSAGE_MAP() |
| | | }; |
| | |
| | | return m_nUnit; |
| | | } |
| | | |
| | | void CPath::setInTime(ULONGLONG time) |
| | | { |
| | | m_timeIn = time; |
| | | } |
| | | |
| | | ULONGLONG CPath::getInTime() |
| | | { |
| | | return m_timeIn; |
| | |
| | | CPath* getHeadPath(); |
| | | unsigned int getEqID(); |
| | | unsigned int getUnit(); |
| | | void setInTime(ULONGLONG time); |
| | | ULONGLONG getInTime(); |
| | | void setOutTime(ULONGLONG time); |
| | | ULONGLONG getOutTime(); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #include "stdafx.h" |
| | | #include "GlassJson.h" |
| | | |
| | | #include <chrono> |
| | | #include <optional> |
| | | #include <sstream> |
| | | #include <limits> |
| | | #include <cstdlib> |
| | | |
| | | #include "CGlass.h" |
| | | #include "CParam.h" |
| | | #include "CJobDataS.h" |
| | | #include "CPath.h" |
| | | |
| | | using namespace SERVO; |
| | | |
| | | // ==================== å°å·¥å
·ï¼å
¼å®¹è JsonCppï¼ ==================== |
| | | |
| | | // å° optional<time_point> 转毫ç§ï¼int64ï¼ï¼å以å符串åå
¥ JSON |
| | | static std::string tp_to_ms_str(std::optional<std::chrono::system_clock::time_point> tp) { |
| | | if (!tp) return std::string(); // 空串表示æ |
| | | using namespace std::chrono; |
| | | long long ms = duration_cast<milliseconds>(tp->time_since_epoch()).count(); |
| | | return std::to_string(ms); |
| | | } |
| | | |
| | | // ä» JSON çå符串/æ°å容å¿å¼è¯»å 64 ä½ï¼ä¼å
string |
| | | static bool get_ll_from_json(const Json::Value& v, const char* key, long long& out) { |
| | | if (!v.isMember(key)) return false; |
| | | const Json::Value& x = v[key]; |
| | | if (x.isString()) { |
| | | const std::string s = x.asString(); |
| | | if (s.empty()) return false; |
| | | char* endp = nullptr; |
| | | errno = 0; |
| | | #if defined(_MSC_VER) |
| | | long long val = _strtoi64(s.c_str(), &endp, 10); |
| | | #else |
| | | long long val = std::strtoll(s.c_str(), &endp, 10); |
| | | #endif |
| | | if (endp && *endp == '\0' && errno == 0) { out = val; return true; } |
| | | // 容éï¼å¦æå
¶å®æ¯æ°åå符串ï¼å¸¦ç©ºæ ¼ï¼ï¼åè¯ä¸æ¬¡ |
| | | try { out = std::stoll(s); return true; } |
| | | catch (...) {} |
| | | return false; |
| | | } |
| | | if (x.isInt()) { out = static_cast<long long>(x.asInt()); return true; } |
| | | if (x.isUInt()) { out = static_cast<long long>(x.asUInt()); return true; } |
| | | if (x.isDouble()) { out = static_cast<long long>(x.asDouble()); return true; } |
| | | return false; |
| | | } |
| | | |
| | | // å 64 ä½ä¸ºå符串ï¼è·¨çæ¬/è·¨è¯è¨æ´ç¨³ï¼ |
| | | static void put_ull_as_str(Json::Value& obj, const char* key, unsigned long long v) { |
| | | obj[key] = Json::Value(std::to_string(v)); |
| | | } |
| | | static void put_ll_as_str(Json::Value& obj, const char* key, long long v) { |
| | | obj[key] = Json::Value(std::to_string(v)); |
| | | } |
| | | |
| | | // ç®å读åï¼å¸¦é»è®¤ï¼ |
| | | static int JInt(const Json::Value& v, const char* k, int d = 0) |
| | | { |
| | | return v.isMember(k) ? v[k].asInt() : d; |
| | | } |
| | | static unsigned JUInt(const Json::Value& v, const char* k, unsigned d = 0) |
| | | { |
| | | return v.isMember(k) ? v[k].asUInt() : d; |
| | | } |
| | | static bool JBool(const Json::Value& v, const char* k, bool d = false) |
| | | { |
| | | return v.isMember(k) ? v[k].asBool() : d; |
| | | } |
| | | static std::string JStr(const Json::Value& v, const char* k, const std::string& d = "") |
| | | { |
| | | return v.isMember(k) ? v[k].asString() : d; |
| | | } |
| | | static double JDouble(const Json::Value& v, const char* k, double d = 0.0) |
| | | { |
| | | return v.isMember(k) ? v[k].asDouble() : d; |
| | | } |
| | | |
| | | // ==================== CGlass -> JSON ==================== |
| | | |
| | | Json::Value GlassJson::ToJson(const CGlass& gConst) { |
| | | CGlass& g = const_cast<CGlass&>(gConst); // è®¸å¤ getter 䏿¯ const |
| | | Json::Value root(Json::objectValue); |
| | | |
| | | // åºæ¬ |
| | | root["id"] = g.getID(); |
| | | root["materials"] = static_cast<int>(g.getType()); // 0/1/2/3 |
| | | root["buddy_id"] = g.getBuddyId(); |
| | | root["has_buddy"] = (g.getBuddy() != nullptr); |
| | | |
| | | int port = 0, slot = 0; |
| | | g.getOrginPort(port, slot); |
| | | root["origin_port"] = port; |
| | | root["origin_slot"] = slot; |
| | | |
| | | root["scheduled"] = g.isScheduledForProcessing() ? true : false; |
| | | root["state"] = static_cast<int>(g.state()); // GlsState -> int |
| | | root["fail_reason"] = g.m_failReason; |
| | | |
| | | // æ¶é´æ³ï¼ä»¥å符串å |
| | | root["t_queued_ms"] = tp_to_ms_str(g.tQueued()); |
| | | root["t_start_ms"] = tp_to_ms_str(g.tStart()); |
| | | root["t_end_ms"] = tp_to_ms_str(g.tEnd()); |
| | | |
| | | // params: vector<CParam> |
| | | { |
| | | Json::Value arr(Json::arrayValue); |
| | | for (auto& p : g.getParams()) { |
| | | Json::Value jp(Json::objectValue); |
| | | jp["id"] = p.getId(); |
| | | jp["name"] = p.getName(); |
| | | jp["unit"] = p.getUnit(); |
| | | int vt = p.getValueType(); // PVT_INT=0, PVT_DOUBLE=1 |
| | | jp["vtype"] = vt; |
| | | if (vt == PVT_INT) jp["iv"] = p.getIntValue(); |
| | | else jp["fv"] = p.getDoubleValue(); |
| | | arr.append(std::move(jp)); |
| | | } |
| | | root["params"] = std::move(arr); |
| | | } |
| | | |
| | | // JobDataS |
| | | { |
| | | CJobDataS& s = *g.getJobDataS(); |
| | | Json::Value js(Json::objectValue); |
| | | js["cassette_seq_no"] = s.getCassetteSequenceNo(); |
| | | js["job_seq_no"] = s.getJobSequenceNo(); |
| | | js["lot_id"] = s.getLotId(); |
| | | js["product_id"] = s.getProductId(); |
| | | js["operation_id"] = s.getOperationId(); |
| | | js["glass1_id"] = s.getGlass1Id(); |
| | | js["glass2_id"] = s.getGlass2Id(); |
| | | |
| | | js["job_type"] = s.getJobType(); |
| | | js["materials_type"] = s.getMaterialsType(); |
| | | js["product_type"] = s.getProductType(); |
| | | js["dummy_type"] = s.getDummyType(); |
| | | js["skip_flag"] = s.getSkipFlag(); |
| | | js["process_flag"] = s.getProcessFlag(); |
| | | js["process_reason"] = s.getProcessResonCode(); |
| | | js["last_glass_flag"] = s.getLastGlassFlag(); |
| | | js["first_glass_flag"] = s.getFirstGlassFlag(); |
| | | |
| | | Json::Value q(Json::arrayValue); |
| | | for (int i = 0; i < 3; i++) q.append(s.getQTime(i)); |
| | | js["qtime"] = std::move(q); |
| | | js["qtime_over_flag"] = s.getQTimeOverFlag(); |
| | | |
| | | js["master_recipe"] = s.getMasterRecipe(); |
| | | Json::Value rids(Json::arrayValue); |
| | | for (int i = 0; i < DEVICE_COUNT; i++) rids.append(s.getDeviceRecipeId(i)); |
| | | js["device_recipe_ids"] = std::move(rids); |
| | | |
| | | js["panel_measure"] = s.getPanelMeasure(); |
| | | js["mode"] = s.getMode(); |
| | | js["slot_unit_select_flag"] = s.getSlotUnitSelectFlag(); |
| | | |
| | | js["source_port_no"] = s.getSourcePortNo(); |
| | | js["source_slot_no"] = s.getSourceSlotNo(); |
| | | js["target_port_no"] = s.getTargetPortNo(); |
| | | js["target_slot_no"] = s.getTargetSlotNo(); |
| | | |
| | | js["product_judge"] = s.getProductJudge(); |
| | | |
| | | root["job_data_s"] = std::move(js); |
| | | } |
| | | |
| | | // pathï¼é¾è¡¨ â æ°ç»ï¼æ¶é´å段以å符串åï¼ |
| | | { |
| | | Json::Value arr(Json::arrayValue); |
| | | if (auto head = g.getPath()) { |
| | | CPath* p = head->getHeadPath(); |
| | | while (p) { |
| | | Json::Value n(Json::objectValue); |
| | | n["eq_id"] = p->getEqID(); |
| | | n["unit"] = p->getUnit(); |
| | | put_ull_as_str(n, "time_in", static_cast<unsigned long long>(p->getInTime())); |
| | | put_ull_as_str(n, "time_out", static_cast<unsigned long long>(p->getOutTime())); |
| | | n["processed"] = p->isProcessEnd() ? true : false; |
| | | n["insp_result"] = static_cast<int>(p->getInspResult()); // 0:not,1:pass,2:fail |
| | | arr.append(std::move(n)); |
| | | p = p->getNext(); |
| | | } |
| | | } |
| | | root["path"] = std::move(arr); |
| | | } |
| | | |
| | | root["payload_version"] = 1; |
| | | return root; |
| | | } |
| | | |
| | | // ==================== JSON -> CGlass ==================== |
| | | |
| | | void GlassJson::FromJson(const Json::Value& root, CGlass& g) { |
| | | // åºæ¬ |
| | | g.setID(JStr(root, "id").c_str()); |
| | | g.setType(static_cast<MaterialsType>(JInt(root, "materials", 0))); |
| | | g.getBuddyId() = JStr(root, "buddy_id"); |
| | | g.setScheduledForProcessing(JBool(root, "scheduled") ? TRUE : FALSE); |
| | | g.m_failReason = JStr(root, "fail_reason"); |
| | | g.setOriginPort(JInt(root, "origin_port", 0), JInt(root, "origin_slot", 0)); |
| | | |
| | | // ç¶æä¸æ¶é´ï¼æ¶é´ä»å符串/æ°å容éè§£æï¼ |
| | | g.m_state = static_cast<GlsState>(JInt(root, "state", 0)); |
| | | long long ms = 0; |
| | | if (get_ll_from_json(root, "t_queued_ms", ms)) g.m_tQueued = std::chrono::system_clock::time_point(std::chrono::milliseconds(ms)); else g.m_tQueued = std::nullopt; |
| | | if (get_ll_from_json(root, "t_start_ms", ms)) g.m_tStart = std::chrono::system_clock::time_point(std::chrono::milliseconds(ms)); else g.m_tStart = std::nullopt; |
| | | if (get_ll_from_json(root, "t_end_ms", ms)) g.m_tEnd = std::chrono::system_clock::time_point(std::chrono::milliseconds(ms)); else g.m_tEnd = std::nullopt; |
| | | |
| | | // params |
| | | g.getParams().clear(); |
| | | if (root.isMember("params") && root["params"].isArray()) { |
| | | for (const auto& jp : root["params"]) { |
| | | const int vt = JInt(jp, "vtype", 0); // 0=int,1=double |
| | | const std::string name = JStr(jp, "name"); |
| | | const std::string id = JStr(jp, "id"); |
| | | const std::string unit = JStr(jp, "unit"); |
| | | if (vt == PVT_INT) { |
| | | CParam p(name.c_str(), id.c_str(), unit.c_str(), JInt(jp, "iv", 0)); |
| | | g.getParams().push_back(std::move(p)); |
| | | } |
| | | else { |
| | | CParam p(name.c_str(), id.c_str(), unit.c_str(), JDouble(jp, "fv", 0.0)); |
| | | g.getParams().push_back(std::move(p)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // JobDataS |
| | | if (root.isMember("job_data_s") && root["job_data_s"].isObject()) { |
| | | const auto& js = root["job_data_s"]; |
| | | CJobDataS* s = g.getJobDataS(); |
| | | |
| | | s->setCassetteSequenceNo(JInt(js, "cassette_seq_no", 0)); |
| | | s->setJobSequenceNo(JInt(js, "job_seq_no", 0)); |
| | | |
| | | s->setLotId(JStr(js, "lot_id").c_str()); |
| | | s->setProductId(JStr(js, "product_id").c_str()); |
| | | s->setOperationId(JStr(js, "operation_id").c_str()); |
| | | s->setGlass1Id(JStr(js, "glass1_id").c_str()); |
| | | s->setGlass2Id(JStr(js, "glass2_id").c_str()); |
| | | |
| | | s->setJobType(JInt(js, "job_type", 0)); |
| | | s->setMaterialsType(JInt(js, "materials_type", 0)); |
| | | s->setProductType(JInt(js, "product_type", 0)); |
| | | s->setDummyType(JInt(js, "dummy_type", 0)); |
| | | s->setSkipFlag(JInt(js, "skip_flag", 0)); |
| | | s->setProcessFlag(JInt(js, "process_flag", 0)); |
| | | s->setProcessResonCode(JInt(js, "process_reason", 0)); |
| | | s->setLastGlassFlag(JInt(js, "last_glass_flag", 0)); |
| | | s->setFirstGlassFlag(JInt(js, "first_glass_flag", 0)); |
| | | |
| | | if (js.isMember("qtime") && js["qtime"].isArray()) { |
| | | for (int i = 0; i < 3; i++) { |
| | | int v = (i < (int)js["qtime"].size()) ? js["qtime"][i].asInt() : 0; |
| | | s->setQTime(i, v); |
| | | } |
| | | } |
| | | else { |
| | | for (int i = 0; i < 3; i++) s->setQTime(i, 0); |
| | | } |
| | | s->setQTimeOverFlag(JInt(js, "qtime_over_flag", 0)); |
| | | |
| | | s->setMasterRecipe(JInt(js, "master_recipe", 0)); |
| | | if (js.isMember("device_recipe_ids") && js["device_recipe_ids"].isArray()) { |
| | | for (int i = 0; i < DEVICE_COUNT; i++) { |
| | | int v = (i < (int)js["device_recipe_ids"].size()) ? js["device_recipe_ids"][i].asInt() : 0; |
| | | s->setDeviceRecipeId(i, static_cast<short>(v)); |
| | | } |
| | | } |
| | | else { |
| | | for (int i = 0; i < DEVICE_COUNT; i++) s->setDeviceRecipeId(i, 0); |
| | | } |
| | | |
| | | s->setPanelMeasure(JStr(js, "panel_measure").c_str()); |
| | | s->setMode(JInt(js, "mode", 0)); |
| | | s->setSlotUnitSelectFlag(JInt(js, "slot_unit_select_flag", 0)); |
| | | |
| | | s->setSourcePortNo(JInt(js, "source_port_no", 0)); |
| | | s->setSourceSlotNo(JInt(js, "source_slot_no", 0)); |
| | | s->setTargetPortNo(JInt(js, "target_port_no", 0)); |
| | | s->setTargetSlotNo(JInt(js, "target_slot_no", 0)); |
| | | |
| | | s->setProductJudge(static_cast<short>(JInt(js, "product_judge", 0))); |
| | | } |
| | | |
| | | // pathï¼é¡ºåºé建é¾ï¼æ¶é´å段以å符串读åï¼ |
| | | if (root.isMember("path") && root["path"].isArray()) { |
| | | for (const auto& n : root["path"]) { |
| | | unsigned eq = JUInt(n, "eq_id", 0); |
| | | unsigned unit = JUInt(n, "unit", 0); |
| | | g.addPath(eq, unit); |
| | | |
| | | CPath* tail = nullptr; |
| | | if (auto head = g.getPath()) tail = head->getTailPath(); |
| | | if (!tail) continue; |
| | | |
| | | long long tin = 0, tout = 0; |
| | | if (get_ll_from_json(n, "time_in", tin)) tail->setInTime(static_cast<ULONGLONG>(tin)); |
| | | if (get_ll_from_json(n, "time_out", tout)) tail->setOutTime(static_cast<ULONGLONG>(tout)); |
| | | tail->setInspResult(static_cast<InspResult>(JInt(n, "insp_result", 0))); |
| | | if (JBool(n, "processed", false)) tail->processEnd(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // ==================== 便æ·å°è£
==================== |
| | | |
| | | std::string GlassJson::ToString(const CGlass& g) { |
| | | Json::FastWriter w; // æ§çï¼ç´§åï¼æ æ ¼å¼ |
| | | // w.omitEndingLineFeed(); // 廿æ«å°¾æ¢è¡ï¼å¦æä½ ç JsonCpp çæ¬æ¯æï¼ |
| | | return w.write(ToJson(g)); |
| | | } |
| | | |
| | | std::string GlassJson::ToPrettyString(const CGlass& g) { |
| | | Json::StyledWriter w; // æ§çï¼ç¾åè¾åº |
| | | return w.write(ToJson(g)); |
| | | } |
| | | |
| | | bool GlassJson::FromString(const std::string& text, CGlass& g, std::string* err) { |
| | | Json::Reader reader; // æ§çè§£æå¨ |
| | | Json::Value j; |
| | | bool ok = reader.parse(text, j, /*collectComments*/ false); |
| | | if (!ok) { |
| | | if (err) *err = reader.getFormatedErrorMessages(); |
| | | return false; |
| | | } |
| | | FromJson(j, g); |
| | | return true; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #pragma once |
| | | #include <string> |
| | | #include <json/json.h> |
| | | |
| | | namespace SERVO { class CGlass; } |
| | | |
| | | /** |
| | | * @brief CGlass <-> JSON çæå°å·¥å
·ç±»ï¼åºäº JsonCppï¼ |
| | | * |
| | | * - ä¸ä¿®æ¹ä»»ä½ç°æç±»å¤´æä»¶ |
| | | * - å
¼å®¹èç JsonCppï¼æ Json::Int64/UInt64/asInt64ï¼ |
| | | * - ååºï¼ToString / ToPrettyString |
| | | * - 读åºï¼FromStringï¼è¿å true/falseï¼ |
| | | */ |
| | | class GlassJson { |
| | | public: |
| | | // æ ¸å¿ï¼å¯¹è±¡ <-> Json::Value |
| | | static Json::Value ToJson(const SERVO::CGlass& g); |
| | | static void FromJson(const Json::Value& j, SERVO::CGlass& g); |
| | | |
| | | // 便æ·ï¼å¯¹è±¡ <-> ç´§åå符串 |
| | | static std::string ToString(const SERVO::CGlass& g); // ç´§å |
| | | static bool FromString(const std::string& text, SERVO::CGlass& g, |
| | | std::string* err = nullptr); // è§£æå¤±è´¥è¿å false |
| | | |
| | | // 便æ·ï¼å¯¹è±¡ <-> ç¾åå符串 |
| | | static std::string ToPrettyString(const SERVO::CGlass& g); // ç¾å |
| | | }; |
| | |
| | | <Optimization>Disabled</Optimization> |
| | | <PreprocessorDefinitions>_WINDOWS;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
| | | <SDLCheck>true</SDLCheck> |
| | | <AdditionalIncludeDirectories>.;..;..\DatabaseSDK\include;..\MELSECSDK\include;.\CCLinkPerformance;.\GridControl;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |
| | | <AdditionalIncludeDirectories>.;..;..\DatabaseSDK\include;..\MELSECSDK\include;.\CCLinkPerformance;.\GridControl;..\jsoncpp\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |
| | | <LanguageStandard>stdcpp17</LanguageStandard> |
| | | </ClCompile> |
| | | <Link> |
| | |
| | | <IntrinsicFunctions>true</IntrinsicFunctions> |
| | | <PreprocessorDefinitions>_WINDOWS;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
| | | <SDLCheck>true</SDLCheck> |
| | | <AdditionalIncludeDirectories>.;..;..\DatabaseSDK\include;..\MELSECSDK\include;.\CCLinkPerformance;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |
| | | <AdditionalIncludeDirectories>.;..;..\DatabaseSDK\include;..\MELSECSDK\include;.\CCLinkPerformance;..\jsoncpp\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |
| | | </ClCompile> |
| | | <Link> |
| | | <SubSystem>Windows</SubSystem> |
| | |
| | | <Text Include="ReadMe.txt" /> |
| | | </ItemGroup> |
| | | <ItemGroup> |
| | | <ClInclude Include="..\jsoncpp\include\json\autolink.h" /> |
| | | <ClInclude Include="..\jsoncpp\include\json\config.h" /> |
| | | <ClInclude Include="..\jsoncpp\include\json\features.h" /> |
| | | <ClInclude Include="..\jsoncpp\include\json\forwards.h" /> |
| | | <ClInclude Include="..\jsoncpp\include\json\json.h" /> |
| | | <ClInclude Include="..\jsoncpp\include\json\reader.h" /> |
| | | <ClInclude Include="..\jsoncpp\include\json\value.h" /> |
| | | <ClInclude Include="..\jsoncpp\include\json\writer.h" /> |
| | | <ClInclude Include="..\jsoncpp\lib_json\json_batchallocator.h" /> |
| | | <ClInclude Include="CBaseDlg.h" /> |
| | | <ClInclude Include="CControlJob.h" /> |
| | | <ClInclude Include="CControlJobDlg.h" /> |
| | |
| | | <ClInclude Include="ChangePasswordDlg.h" /> |
| | | <ClInclude Include="CMyStatusbar.h" /> |
| | | <ClInclude Include="CPageCollectionEvent.h" /> |
| | | <ClInclude Include="CPageGlassList.h" /> |
| | | <ClInclude Include="CPageLinkSignal.h" /> |
| | | <ClInclude Include="CPageReport.h" /> |
| | | <ClInclude Include="CPageVarialbles.h" /> |
| | |
| | | <ClInclude Include="CPortStatusReport.h" /> |
| | | <ClInclude Include="CRobotTaskDlg.h" /> |
| | | <ClInclude Include="CVariable.h" /> |
| | | <ClInclude Include="DeviceRecipeParamDlg.h" /> |
| | | <ClInclude Include="GlassJson.h" /> |
| | | <ClInclude Include="GridControl\CellRange.h" /> |
| | | <ClInclude Include="GridControl\GridCell.h" /> |
| | | <ClInclude Include="GridControl\GridCellBase.h" /> |
| | |
| | | <ClInclude Include="VerticalLine.h" /> |
| | | </ItemGroup> |
| | | <ItemGroup> |
| | | <ClCompile Include="..\jsoncpp\lib_json\json_reader.cpp"> |
| | | <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> |
| | | <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> |
| | | </ClCompile> |
| | | <ClCompile Include="..\jsoncpp\lib_json\json_value.cpp"> |
| | | <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> |
| | | <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> |
| | | </ClCompile> |
| | | <ClCompile Include="..\jsoncpp\lib_json\json_writer.cpp"> |
| | | <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> |
| | | <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> |
| | | </ClCompile> |
| | | <ClCompile Include="CBaseDlg.cpp" /> |
| | | <ClCompile Include="CControlJob.cpp" /> |
| | | <ClCompile Include="CControlJobDlg.cpp" /> |
| | |
| | | <ClCompile Include="ChangePasswordDlg.cpp" /> |
| | | <ClCompile Include="CMyStatusbar.cpp" /> |
| | | <ClCompile Include="CPageCollectionEvent.cpp" /> |
| | | <ClCompile Include="CPageGlassList.cpp" /> |
| | | <ClCompile Include="CPageLinkSignal.cpp" /> |
| | | <ClCompile Include="CPageReport.cpp" /> |
| | | <ClCompile Include="CPageVarialbles.cpp" /> |
| | |
| | | <ClCompile Include="CPortStatusReport.cpp" /> |
| | | <ClCompile Include="CRobotTaskDlg.cpp" /> |
| | | <ClCompile Include="CVariable.cpp" /> |
| | | <ClCompile Include="DeviceRecipeParamDlg.cpp" /> |
| | | <ClCompile Include="GlassJson.cpp" /> |
| | | <ClCompile Include="GridControl\GridCell.cpp" /> |
| | | <ClCompile Include="GridControl\GridCellBase.cpp" /> |
| | | <ClCompile Include="GridControl\GridCellButton.cpp" /> |
| | |
| | | <ResourceCompile Include="Servo.rc" /> |
| | | </ItemGroup> |
| | | <ItemGroup> |
| | | <None Include="..\jsoncpp\lib_json\json_internalarray.inl" /> |
| | | <None Include="..\jsoncpp\lib_json\json_internalmap.inl" /> |
| | | <None Include="..\jsoncpp\lib_json\json_valueiterator.inl" /> |
| | | <None Include="..\jsoncpp\lib_json\sconscript" /> |
| | | <None Include="packages.config" /> |
| | | <None Include="res\Servo.rc2" /> |
| | | </ItemGroup> |
| | |
| | | <ClCompile Include="CExpandableListCtrl.cpp" /> |
| | | <ClCompile Include="CControlJobDlg.cpp" /> |
| | | <ClCompile Include="CParam.cpp" /> |
| | | <ClCompile Include="DeviceRecipeParamDlg.cpp" /> |
| | | <ClCompile Include="CPageGlassList.cpp" /> |
| | | <ClCompile Include="GlassJson.cpp" /> |
| | | <ClCompile Include="..\jsoncpp\lib_json\json_reader.cpp"> |
| | | <Filter>JsonCpp</Filter> |
| | | </ClCompile> |
| | | <ClCompile Include="..\jsoncpp\lib_json\json_value.cpp"> |
| | | <Filter>JsonCpp</Filter> |
| | | </ClCompile> |
| | | <ClCompile Include="..\jsoncpp\lib_json\json_writer.cpp"> |
| | | <Filter>JsonCpp</Filter> |
| | | </ClCompile> |
| | | </ItemGroup> |
| | | <ItemGroup> |
| | | <ClInclude Include="AlarmManager.h" /> |
| | |
| | | <ClInclude Include="CExpandableListCtrl.h" /> |
| | | <ClInclude Include="CControlJobDlg.h" /> |
| | | <ClInclude Include="CParam.h" /> |
| | | <ClInclude Include="DeviceRecipeParamDlg.h" /> |
| | | <ClInclude Include="CPageGlassList.h" /> |
| | | <ClInclude Include="GlassJson.h" /> |
| | | <ClInclude Include="..\jsoncpp\include\json\autolink.h"> |
| | | <Filter>JsonCpp</Filter> |
| | | </ClInclude> |
| | | <ClInclude Include="..\jsoncpp\include\json\config.h"> |
| | | <Filter>JsonCpp</Filter> |
| | | </ClInclude> |
| | | <ClInclude Include="..\jsoncpp\include\json\features.h"> |
| | | <Filter>JsonCpp</Filter> |
| | | </ClInclude> |
| | | <ClInclude Include="..\jsoncpp\include\json\forwards.h"> |
| | | <Filter>JsonCpp</Filter> |
| | | </ClInclude> |
| | | <ClInclude Include="..\jsoncpp\include\json\json.h"> |
| | | <Filter>JsonCpp</Filter> |
| | | </ClInclude> |
| | | <ClInclude Include="..\jsoncpp\include\json\reader.h"> |
| | | <Filter>JsonCpp</Filter> |
| | | </ClInclude> |
| | | <ClInclude Include="..\jsoncpp\include\json\value.h"> |
| | | <Filter>JsonCpp</Filter> |
| | | </ClInclude> |
| | | <ClInclude Include="..\jsoncpp\include\json\writer.h"> |
| | | <Filter>JsonCpp</Filter> |
| | | </ClInclude> |
| | | <ClInclude Include="..\jsoncpp\lib_json\json_batchallocator.h"> |
| | | <Filter>JsonCpp</Filter> |
| | | </ClInclude> |
| | | </ItemGroup> |
| | | <ItemGroup> |
| | | <ResourceCompile Include="Servo.rc" /> |
| | |
| | | <ItemGroup> |
| | | <None Include="packages.config" /> |
| | | <None Include="res\Servo.rc2" /> |
| | | <None Include="..\jsoncpp\lib_json\json_internalarray.inl"> |
| | | <Filter>JsonCpp</Filter> |
| | | </None> |
| | | <None Include="..\jsoncpp\lib_json\json_internalmap.inl"> |
| | | <Filter>JsonCpp</Filter> |
| | | </None> |
| | | <None Include="..\jsoncpp\lib_json\json_valueiterator.inl"> |
| | | <Filter>JsonCpp</Filter> |
| | | </None> |
| | | <None Include="..\jsoncpp\lib_json\sconscript"> |
| | | <Filter>JsonCpp</Filter> |
| | | </None> |
| | | </ItemGroup> |
| | | <ItemGroup> |
| | | <Text Include="ReadMe.txt" /> |
| | |
| | | <Filter Include="GridControl\NewCellTypes"> |
| | | <UniqueIdentifier>{52ebd40d-fb71-4a53-93ab-5b93670c706d}</UniqueIdentifier> |
| | | </Filter> |
| | | <Filter Include="JsonCpp"> |
| | | <UniqueIdentifier>{a877af37-2f78-484f-b1bb-276edbbcd694}</UniqueIdentifier> |
| | | </Filter> |
| | | </ItemGroup> |
| | | </Project> |
| | |
| | | m_pPanelAttributes = nullptr; |
| | | m_pPageGraph1 = nullptr; |
| | | m_pPageGraph2 = nullptr; |
| | | m_pPageGlassList = nullptr; |
| | | m_pPageAlarm = nullptr; |
| | | m_pPageLog = nullptr; |
| | | m_pPageTransferLog = nullptr; |
| | |
| | | m_pPageGraph1->Create(IDD_PAGE_GRAPH1, this); |
| | | m_pPageGraph2 = new CPageGraph2(); |
| | | m_pPageGraph2->Create(IDD_PAGE_GRAPH2, this); |
| | | m_pPageGlassList = new CPageGlassList(); |
| | | m_pPageGlassList->Create(IDD_PAGE_GLASS_LIST, this); |
| | | m_pPageRecipe = new CPageRecipe(); |
| | | m_pPageRecipe->Create(IDD_PAGE_RECIPE, this); |
| | | m_pPageAlarm = new CPageAlarm(); |
| | |
| | | m_pTab->SetItemMarginLeft(18); |
| | | m_pTab->AddItem("״̬ͼ", FALSE); |
| | | m_pTab->AddItem("è¿æ¥å¾", TRUE); |
| | | m_pTab->AddItem("Glass", TRUE); |
| | | m_pTab->AddItem("é
æ¹", TRUE); |
| | | m_pTab->AddItem("æ¥è¦", TRUE); |
| | | m_pTab->AddItem("æ¥å¿", TRUE); |
| | | m_pTab->AddItem("æ¬è¿", TRUE); |
| | | m_pTab->AddItem("æ¬è¿ä»»å¡", TRUE); |
| | | m_pTab->SetCurSel(0); |
| | | m_pTab->SetBkgndColor(RGB(222, 222, 222)); |
| | | ShowChildPage(0); |
| | |
| | | m_pPageGraph2 = nullptr; |
| | | } |
| | | |
| | | if (m_pPageGlassList != nullptr) { |
| | | m_pPageGlassList->DestroyWindow(); |
| | | delete m_pPageGlassList; |
| | | m_pPageGlassList = nullptr; |
| | | } |
| | | |
| | | if (m_pPageRecipe != nullptr) { |
| | | m_pPageRecipe->DestroyWindow(); |
| | | delete m_pPageRecipe; |
| | |
| | | if (GetDlgItem(IDC_TAB1) == nullptr) return; |
| | | if (m_pPageGraph1 == nullptr) return; |
| | | if (m_pPageGraph2 == nullptr) return; |
| | | if (m_pPageGlassList == nullptr) return; |
| | | if (m_pPageRecipe == nullptr) return; |
| | | if (m_pPageAlarm == nullptr) return; |
| | | if (m_pPageLog == nullptr) return; |
| | |
| | | |
| | | m_pPageGraph1->MoveWindow(x, y, rcClient.Width() - x, y2 - y); |
| | | m_pPageGraph2->MoveWindow(x, y, rcClient.Width() - x, y2 - y); |
| | | m_pPageGlassList->MoveWindow(x, y, rcClient.Width() - x, y2 - y); |
| | | m_pPageRecipe->MoveWindow(x, y, rcClient.Width() - x, y2 - y); |
| | | m_pPageAlarm->MoveWindow(x, y, rcClient.Width() - x, y2 - y); |
| | | m_pPageLog->MoveWindow(x, y, rcClient.Width() - x, y2 - y); |
| | |
| | | |
| | | void CServoDlg::ShowChildPage(int index) |
| | | { |
| | | ASSERT(0 <= index && index < 6); |
| | | static CWnd* pPages[] = { m_pPageGraph1, m_pPageGraph2, m_pPageRecipe, m_pPageAlarm, m_pPageLog, m_pPageTransferLog }; |
| | | for (int i = 0; i < 6; i++) { |
| | | static CWnd* pPages[] = { m_pPageGraph1, m_pPageGraph2, m_pPageGlassList, |
| | | m_pPageRecipe, m_pPageAlarm, m_pPageLog, m_pPageTransferLog }; |
| | | ASSERT(0 <= index && index < sizeof(pPages) / sizeof(CWnd*)); |
| | | |
| | | for (int i = 0; i < sizeof(pPages) / sizeof(CWnd*); i++) { |
| | | pPages[i]->ShowWindow(i == index ? SW_SHOW : SW_HIDE); |
| | | } |
| | | } |
| | |
| | | #include "TopToolbar.h" |
| | | #include "CMyStatusbar.h" |
| | | #include "CRobotTaskDlg.h" |
| | | #include "CPageGlassList.h" |
| | | |
| | | |
| | | // CServoDlg å¯¹è¯æ¡ |
| | |
| | | CRobotTaskDlg* m_pRobotTaskDlg; |
| | | CPageGraph1* m_pPageGraph1; |
| | | CPageGraph2* m_pPageGraph2; |
| | | CPageGlassList* m_pPageGlassList; |
| | | CPageRecipe* m_pPageRecipe; |
| | | CPageAlarm* m_pPageAlarm; |
| | | CPageLog* m_pPageLog; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #ifndef JSON_AUTOLINK_H_INCLUDED |
| | | # define JSON_AUTOLINK_H_INCLUDED |
| | | |
| | | # include "config.h" |
| | | |
| | | # ifdef JSON_IN_CPPTL |
| | | # include <cpptl/cpptl_autolink.h> |
| | | # endif |
| | | |
| | | # if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && !defined(JSON_IN_CPPTL) |
| | | # define CPPTL_AUTOLINK_NAME "json" |
| | | # undef CPPTL_AUTOLINK_DLL |
| | | # ifdef JSON_DLL |
| | | # define CPPTL_AUTOLINK_DLL |
| | | # endif |
| | | # include "autolink.h" |
| | | # endif |
| | | |
| | | #endif // JSON_AUTOLINK_H_INCLUDED |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #ifndef JSON_CONFIG_H_INCLUDED |
| | | # define JSON_CONFIG_H_INCLUDED |
| | | |
| | | /// If defined, indicates that json library is embedded in CppTL library. |
| | | //# define JSON_IN_CPPTL 1 |
| | | |
| | | /// If defined, indicates that json may leverage CppTL library |
| | | //# define JSON_USE_CPPTL 1 |
| | | /// If defined, indicates that cpptl vector based map should be used instead of std::map |
| | | /// as Value container. |
| | | //# define JSON_USE_CPPTL_SMALLMAP 1 |
| | | /// If defined, indicates that Json specific container should be used |
| | | /// (hash table & simple deque container with customizable allocator). |
| | | /// THIS FEATURE IS STILL EXPERIMENTAL! |
| | | //# define JSON_VALUE_USE_INTERNAL_MAP 1 |
| | | /// Force usage of standard new/malloc based allocator instead of memory pool based allocator. |
| | | /// The memory pools allocator used optimization (initializing Value and ValueInternalLink |
| | | /// as if it was a POD) that may cause some validation tool to report errors. |
| | | /// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. |
| | | //# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 |
| | | |
| | | /// If defined, indicates that Json use exception to report invalid type manipulation |
| | | /// instead of C assert macro. |
| | | # define JSON_USE_EXCEPTION 1 |
| | | |
| | | # ifdef JSON_IN_CPPTL |
| | | # include <cpptl/config.h> |
| | | # ifndef JSON_USE_CPPTL |
| | | # define JSON_USE_CPPTL 1 |
| | | # endif |
| | | # endif |
| | | |
| | | # ifdef JSON_IN_CPPTL |
| | | # define JSON_API CPPTL_API |
| | | # elif defined(JSON_DLL_BUILD) |
| | | # define JSON_API __declspec(dllexport) |
| | | # elif defined(JSON_DLL) |
| | | # define JSON_API __declspec(dllimport) |
| | | # else |
| | | # define JSON_API |
| | | # endif |
| | | |
| | | #endif // JSON_CONFIG_H_INCLUDED |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #ifndef CPPTL_JSON_FEATURES_H_INCLUDED |
| | | # define CPPTL_JSON_FEATURES_H_INCLUDED |
| | | |
| | | # include "forwards.h" |
| | | |
| | | namespace Json { |
| | | |
| | | /** \brief Configuration passed to reader and writer. |
| | | * This configuration object can be used to force the Reader or Writer |
| | | * to behave in a standard conforming way. |
| | | */ |
| | | class JSON_API Features |
| | | { |
| | | public: |
| | | /** \brief A configuration that allows all features and assumes all strings are UTF-8. |
| | | * - C & C++ comments are allowed |
| | | * - Root object can be any JSON value |
| | | * - Assumes Value strings are encoded in UTF-8 |
| | | */ |
| | | static Features all(); |
| | | |
| | | /** \brief A configuration that is strictly compatible with the JSON specification. |
| | | * - Comments are forbidden. |
| | | * - Root object must be either an array or an object value. |
| | | * - Assumes Value strings are encoded in UTF-8 |
| | | */ |
| | | static Features strictMode(); |
| | | |
| | | /** \brief Initialize the configuration like JsonConfig::allFeatures; |
| | | */ |
| | | Features(); |
| | | |
| | | /// \c true if comments are allowed. Default: \c true. |
| | | bool allowComments_; |
| | | |
| | | /// \c true if root must be either an array or an object value. Default: \c false. |
| | | bool strictRoot_; |
| | | }; |
| | | |
| | | } // namespace Json |
| | | |
| | | #endif // CPPTL_JSON_FEATURES_H_INCLUDED |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #ifndef JSON_FORWARDS_H_INCLUDED |
| | | # define JSON_FORWARDS_H_INCLUDED |
| | | |
| | | # include "config.h" |
| | | |
| | | namespace Json { |
| | | |
| | | // writer.h |
| | | class FastWriter; |
| | | class StyledWriter; |
| | | |
| | | // reader.h |
| | | class Reader; |
| | | |
| | | // features.h |
| | | class Features; |
| | | |
| | | // value.h |
| | | typedef int Int; |
| | | typedef unsigned int UInt; |
| | | class StaticString; |
| | | class Path; |
| | | class PathArgument; |
| | | class Value; |
| | | class ValueIteratorBase; |
| | | class ValueIterator; |
| | | class ValueConstIterator; |
| | | #ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | class ValueAllocator; |
| | | class ValueMapAllocator; |
| | | class ValueInternalLink; |
| | | class ValueInternalArray; |
| | | class ValueInternalMap; |
| | | #endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | |
| | | } // namespace Json |
| | | |
| | | |
| | | #endif // JSON_FORWARDS_H_INCLUDED |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #ifndef JSON_JSON_H_INCLUDED |
| | | # define JSON_JSON_H_INCLUDED |
| | | |
| | | # include "autolink.h" |
| | | # include "value.h" |
| | | # include "reader.h" |
| | | # include "writer.h" |
| | | # include "features.h" |
| | | |
| | | #endif // JSON_JSON_H_INCLUDED |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #ifndef CPPTL_JSON_READER_H_INCLUDED |
| | | # define CPPTL_JSON_READER_H_INCLUDED |
| | | |
| | | # include "features.h" |
| | | # include "value.h" |
| | | # include <deque> |
| | | # include <stack> |
| | | # include <string> |
| | | # include <iostream> |
| | | |
| | | namespace Json { |
| | | |
| | | /** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a Value. |
| | | * |
| | | */ |
| | | class JSON_API Reader |
| | | { |
| | | public: |
| | | typedef char Char; |
| | | typedef const Char *Location; |
| | | |
| | | /** \brief Constructs a Reader allowing all features |
| | | * for parsing. |
| | | */ |
| | | Reader(); |
| | | |
| | | /** \brief Constructs a Reader allowing the specified feature set |
| | | * for parsing. |
| | | */ |
| | | Reader( const Features &features ); |
| | | |
| | | /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document. |
| | | * \param document UTF-8 encoded string containing the document to read. |
| | | * \param root [out] Contains the root value of the document if it was |
| | | * successfully parsed. |
| | | * \param collectComments \c true to collect comment and allow writing them back during |
| | | * serialization, \c false to discard comments. |
| | | * This parameter is ignored if Features::allowComments_ |
| | | * is \c false. |
| | | * \return \c true if the document was successfully parsed, \c false if an error occurred. |
| | | */ |
| | | bool parse( const std::string &document, |
| | | Value &root, |
| | | bool collectComments = true ); |
| | | |
| | | /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document. |
| | | * \param document UTF-8 encoded string containing the document to read. |
| | | * \param root [out] Contains the root value of the document if it was |
| | | * successfully parsed. |
| | | * \param collectComments \c true to collect comment and allow writing them back during |
| | | * serialization, \c false to discard comments. |
| | | * This parameter is ignored if Features::allowComments_ |
| | | * is \c false. |
| | | * \return \c true if the document was successfully parsed, \c false if an error occurred. |
| | | */ |
| | | bool parse( const char *beginDoc, const char *endDoc, |
| | | Value &root, |
| | | bool collectComments = true ); |
| | | |
| | | /// \brief Parse from input stream. |
| | | /// \see Json::operator>>(std::istream&, Json::Value&). |
| | | bool parse( std::istream &is, |
| | | Value &root, |
| | | bool collectComments = true ); |
| | | |
| | | /** \brief Returns a user friendly string that list errors in the parsed document. |
| | | * \return Formatted error message with the list of errors with their location in |
| | | * the parsed document. An empty string is returned if no error occurred |
| | | * during parsing. |
| | | */ |
| | | std::string getFormatedErrorMessages() const; |
| | | |
| | | private: |
| | | enum TokenType |
| | | { |
| | | tokenEndOfStream = 0, |
| | | tokenObjectBegin, |
| | | tokenObjectEnd, |
| | | tokenArrayBegin, |
| | | tokenArrayEnd, |
| | | tokenString, |
| | | tokenNumber, |
| | | tokenTrue, |
| | | tokenFalse, |
| | | tokenNull, |
| | | tokenArraySeparator, |
| | | tokenMemberSeparator, |
| | | tokenComment, |
| | | tokenError |
| | | }; |
| | | |
| | | class Token |
| | | { |
| | | public: |
| | | TokenType type_; |
| | | Location start_; |
| | | Location end_; |
| | | }; |
| | | |
| | | class ErrorInfo |
| | | { |
| | | public: |
| | | Token token_; |
| | | std::string message_; |
| | | Location extra_; |
| | | }; |
| | | |
| | | typedef std::deque<ErrorInfo> Errors; |
| | | |
| | | bool expectToken( TokenType type, Token &token, const char *message ); |
| | | bool readToken( Token &token ); |
| | | void skipSpaces(); |
| | | bool match( Location pattern, |
| | | int patternLength ); |
| | | bool readComment(); |
| | | bool readCStyleComment(); |
| | | bool readCppStyleComment(); |
| | | bool readString(); |
| | | void readNumber(); |
| | | bool readValue(); |
| | | bool readObject( Token &token ); |
| | | bool readArray( Token &token ); |
| | | bool decodeNumber( Token &token ); |
| | | bool decodeString( Token &token ); |
| | | bool decodeString( Token &token, std::string &decoded ); |
| | | bool decodeDouble( Token &token ); |
| | | bool decodeUnicodeCodePoint( Token &token, |
| | | Location ¤t, |
| | | Location end, |
| | | unsigned int &unicode ); |
| | | bool decodeUnicodeEscapeSequence( Token &token, |
| | | Location ¤t, |
| | | Location end, |
| | | unsigned int &unicode ); |
| | | bool addError( const std::string &message, |
| | | Token &token, |
| | | Location extra = 0 ); |
| | | bool recoverFromError( TokenType skipUntilToken ); |
| | | bool addErrorAndRecover( const std::string &message, |
| | | Token &token, |
| | | TokenType skipUntilToken ); |
| | | void skipUntilSpace(); |
| | | Value ¤tValue(); |
| | | Char getNextChar(); |
| | | void getLocationLineAndColumn( Location location, |
| | | int &line, |
| | | int &column ) const; |
| | | std::string getLocationLineAndColumn( Location location ) const; |
| | | void addComment( Location begin, |
| | | Location end, |
| | | CommentPlacement placement ); |
| | | void skipCommentTokens( Token &token ); |
| | | |
| | | typedef std::stack<Value *> Nodes; |
| | | Nodes nodes_; |
| | | Errors errors_; |
| | | std::string document_; |
| | | Location begin_; |
| | | Location end_; |
| | | Location current_; |
| | | Location lastValueEnd_; |
| | | Value *lastValue_; |
| | | std::string commentsBefore_; |
| | | Features features_; |
| | | bool collectComments_; |
| | | }; |
| | | |
| | | /** \brief Read from 'sin' into 'root'. |
| | | |
| | | Always keep comments from the input JSON. |
| | | |
| | | This can be used to read a file into a particular sub-object. |
| | | For example: |
| | | \code |
| | | Json::Value root; |
| | | cin >> root["dir"]["file"]; |
| | | cout << root; |
| | | \endcode |
| | | Result: |
| | | \verbatim |
| | | { |
| | | "dir": { |
| | | "file": { |
| | | // The input stream JSON would be nested here. |
| | | } |
| | | } |
| | | } |
| | | \endverbatim |
| | | \throw std::exception on parse error. |
| | | \see Json::operator<<() |
| | | */ |
| | | std::istream& operator>>( std::istream&, Value& ); |
| | | |
| | | } // namespace Json |
| | | |
| | | #endif // CPPTL_JSON_READER_H_INCLUDED |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #ifndef CPPTL_JSON_H_INCLUDED |
| | | # define CPPTL_JSON_H_INCLUDED |
| | | |
| | | # include "forwards.h" |
| | | # include <string> |
| | | # include <vector> |
| | | |
| | | # ifndef JSON_USE_CPPTL_SMALLMAP |
| | | # include <map> |
| | | # else |
| | | # include <cpptl/smallmap.h> |
| | | # endif |
| | | # ifdef JSON_USE_CPPTL |
| | | # include <cpptl/forwards.h> |
| | | # endif |
| | | |
| | | /** \brief JSON (JavaScript Object Notation). |
| | | */ |
| | | namespace Json { |
| | | |
| | | /** \brief Type of the value held by a Value object. |
| | | */ |
| | | enum ValueType |
| | | { |
| | | nullValue = 0, ///< 'null' value |
| | | intValue, ///< signed integer value |
| | | uintValue, ///< unsigned integer value |
| | | realValue, ///< double value |
| | | stringValue, ///< UTF-8 string value |
| | | booleanValue, ///< bool value |
| | | arrayValue, ///< array value (ordered list) |
| | | objectValue ///< object value (collection of name/value pairs). |
| | | }; |
| | | |
| | | enum CommentPlacement |
| | | { |
| | | commentBefore = 0, ///< a comment placed on the line before a value |
| | | commentAfterOnSameLine, ///< a comment just after a value on the same line |
| | | commentAfter, ///< a comment on the line after a value (only make sense for root value) |
| | | numberOfCommentPlacement |
| | | }; |
| | | |
| | | //# ifdef JSON_USE_CPPTL |
| | | // typedef CppTL::AnyEnumerator<const char *> EnumMemberNames; |
| | | // typedef CppTL::AnyEnumerator<const Value &> EnumValues; |
| | | //# endif |
| | | |
| | | /** \brief Lightweight wrapper to tag static string. |
| | | * |
| | | * Value constructor and objectValue member assignement takes advantage of the |
| | | * StaticString and avoid the cost of string duplication when storing the |
| | | * string or the member name. |
| | | * |
| | | * Example of usage: |
| | | * \code |
| | | * Json::Value aValue( StaticString("some text") ); |
| | | * Json::Value object; |
| | | * static const StaticString code("code"); |
| | | * object[code] = 1234; |
| | | * \endcode |
| | | */ |
| | | class JSON_API StaticString |
| | | { |
| | | public: |
| | | explicit StaticString( const char *czstring ) |
| | | : str_( czstring ) |
| | | { |
| | | } |
| | | |
| | | operator const char *() const |
| | | { |
| | | return str_; |
| | | } |
| | | |
| | | const char *c_str() const |
| | | { |
| | | return str_; |
| | | } |
| | | |
| | | private: |
| | | const char *str_; |
| | | }; |
| | | |
| | | /** \brief Represents a <a HREF="http://www.json.org">JSON</a> value. |
| | | * |
| | | * This class is a discriminated union wrapper that can represents a: |
| | | * - signed integer [range: Value::minInt - Value::maxInt] |
| | | * - unsigned integer (range: 0 - Value::maxUInt) |
| | | * - double |
| | | * - UTF-8 string |
| | | * - boolean |
| | | * - 'null' |
| | | * - an ordered list of Value |
| | | * - collection of name/value pairs (javascript object) |
| | | * |
| | | * The type of the held value is represented by a #ValueType and |
| | | * can be obtained using type(). |
| | | * |
| | | * values of an #objectValue or #arrayValue can be accessed using operator[]() methods. |
| | | * Non const methods will automatically create the a #nullValue element |
| | | * if it does not exist. |
| | | * The sequence of an #arrayValue will be automatically resize and initialized |
| | | * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. |
| | | * |
| | | * The get() methods can be used to obtanis default value in the case the required element |
| | | * does not exist. |
| | | * |
| | | * It is possible to iterate over the list of a #objectValue values using |
| | | * the getMemberNames() method. |
| | | */ |
| | | class JSON_API Value |
| | | { |
| | | friend class ValueIteratorBase; |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | friend class ValueInternalLink; |
| | | friend class ValueInternalMap; |
| | | # endif |
| | | public: |
| | | typedef std::vector<std::string> Members; |
| | | typedef ValueIterator iterator; |
| | | typedef ValueConstIterator const_iterator; |
| | | typedef Json::UInt UInt; |
| | | typedef Json::Int Int; |
| | | typedef UInt ArrayIndex; |
| | | |
| | | static const Value null; |
| | | static const Int minInt; |
| | | static const Int maxInt; |
| | | static const UInt maxUInt; |
| | | |
| | | private: |
| | | #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION |
| | | # ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | class CZString |
| | | { |
| | | public: |
| | | enum DuplicationPolicy |
| | | { |
| | | noDuplication = 0, |
| | | duplicate, |
| | | duplicateOnCopy |
| | | }; |
| | | CZString( int index ); |
| | | CZString( const char *cstr, DuplicationPolicy allocate ); |
| | | CZString( const CZString &other ); |
| | | ~CZString(); |
| | | CZString &operator =( const CZString &other ); |
| | | bool operator<( const CZString &other ) const; |
| | | bool operator==( const CZString &other ) const; |
| | | int index() const; |
| | | const char *c_str() const; |
| | | bool isStaticString() const; |
| | | private: |
| | | void swap( CZString &other ); |
| | | const char *cstr_; |
| | | int index_; |
| | | }; |
| | | |
| | | public: |
| | | # ifndef JSON_USE_CPPTL_SMALLMAP |
| | | typedef std::map<CZString, Value> ObjectValues; |
| | | # else |
| | | typedef CppTL::SmallMap<CZString, Value> ObjectValues; |
| | | # endif // ifndef JSON_USE_CPPTL_SMALLMAP |
| | | # endif // ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | #endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION |
| | | |
| | | public: |
| | | /** \brief Create a default Value of the given type. |
| | | |
| | | This is a very useful constructor. |
| | | To create an empty array, pass arrayValue. |
| | | To create an empty object, pass objectValue. |
| | | Another Value can then be set to this one by assignment. |
| | | This is useful since clear() and resize() will not alter types. |
| | | |
| | | Examples: |
| | | \code |
| | | Json::Value null_value; // null |
| | | Json::Value arr_value(Json::arrayValue); // [] |
| | | Json::Value obj_value(Json::objectValue); // {} |
| | | \endcode |
| | | */ |
| | | Value( ValueType type = nullValue ); |
| | | Value( Int value ); |
| | | Value( UInt value ); |
| | | Value( double value ); |
| | | Value( const char *value ); |
| | | Value( const char *beginValue, const char *endValue ); |
| | | /** \brief Constructs a value from a static string. |
| | | |
| | | * Like other value string constructor but do not duplicate the string for |
| | | * internal storage. The given string must remain alive after the call to this |
| | | * constructor. |
| | | * Example of usage: |
| | | * \code |
| | | * Json::Value aValue( StaticString("some text") ); |
| | | * \endcode |
| | | */ |
| | | Value( const StaticString &value ); |
| | | Value( const std::string &value ); |
| | | # ifdef JSON_USE_CPPTL |
| | | Value( const CppTL::ConstString &value ); |
| | | # endif |
| | | Value( bool value ); |
| | | Value( const Value &other ); |
| | | ~Value(); |
| | | |
| | | Value &operator=( const Value &other ); |
| | | /// Swap values. |
| | | /// \note Currently, comments are intentionally not swapped, for |
| | | /// both logic and efficiency. |
| | | void swap( Value &other ); |
| | | |
| | | ValueType type() const; |
| | | |
| | | bool operator <( const Value &other ) const; |
| | | bool operator <=( const Value &other ) const; |
| | | bool operator >=( const Value &other ) const; |
| | | bool operator >( const Value &other ) const; |
| | | |
| | | bool operator ==( const Value &other ) const; |
| | | bool operator !=( const Value &other ) const; |
| | | |
| | | int compare( const Value &other ); |
| | | |
| | | const char *asCString() const; |
| | | std::string asString() const; |
| | | # ifdef JSON_USE_CPPTL |
| | | CppTL::ConstString asConstString() const; |
| | | # endif |
| | | Int asInt() const; |
| | | UInt asUInt() const; |
| | | double asDouble() const; |
| | | bool asBool() const; |
| | | |
| | | bool isNull() const; |
| | | bool isBool() const; |
| | | bool isInt() const; |
| | | bool isUInt() const; |
| | | bool isIntegral() const; |
| | | bool isDouble() const; |
| | | bool isNumeric() const; |
| | | bool isString() const; |
| | | bool isArray() const; |
| | | bool isObject() const; |
| | | |
| | | bool isConvertibleTo( ValueType other ) const; |
| | | |
| | | /// Number of values in array or object |
| | | UInt size() const; |
| | | |
| | | /// \brief Return true if empty array, empty object, or null; |
| | | /// otherwise, false. |
| | | bool empty() const; |
| | | |
| | | /// Return isNull() |
| | | bool operator!() const; |
| | | |
| | | /// Remove all object members and array elements. |
| | | /// \pre type() is arrayValue, objectValue, or nullValue |
| | | /// \post type() is unchanged |
| | | void clear(); |
| | | |
| | | /// Resize the array to size elements. |
| | | /// New elements are initialized to null. |
| | | /// May only be called on nullValue or arrayValue. |
| | | /// \pre type() is arrayValue or nullValue |
| | | /// \post type() is arrayValue |
| | | void resize( UInt size ); |
| | | |
| | | /// Access an array element (zero based index ). |
| | | /// If the array contains less than index element, then null value are inserted |
| | | /// in the array so that its size is index+1. |
| | | /// (You may need to say 'value[0u]' to get your compiler to distinguish |
| | | /// this from the operator[] which takes a string.) |
| | | Value &operator[]( UInt index ); |
| | | /// Access an array element (zero based index ) |
| | | /// (You may need to say 'value[0u]' to get your compiler to distinguish |
| | | /// this from the operator[] which takes a string.) |
| | | const Value &operator[]( UInt index ) const; |
| | | /// If the array contains at least index+1 elements, returns the element value, |
| | | /// otherwise returns defaultValue. |
| | | Value get( UInt index, |
| | | const Value &defaultValue ) const; |
| | | /// Return true if index < size(). |
| | | bool isValidIndex( UInt index ) const; |
| | | /// \brief Append value to array at the end. |
| | | /// |
| | | /// Equivalent to jsonvalue[jsonvalue.size()] = value; |
| | | Value &append( const Value &value ); |
| | | |
| | | /// Access an object value by name, create a null member if it does not exist. |
| | | Value &operator[]( const char *key ); |
| | | /// Access an object value by name, returns null if there is no member with that name. |
| | | const Value &operator[]( const char *key ) const; |
| | | /// Access an object value by name, create a null member if it does not exist. |
| | | Value &operator[]( const std::string &key ); |
| | | /// Access an object value by name, returns null if there is no member with that name. |
| | | const Value &operator[]( const std::string &key ) const; |
| | | /** \brief Access an object value by name, create a null member if it does not exist. |
| | | |
| | | * If the object as no entry for that name, then the member name used to store |
| | | * the new entry is not duplicated. |
| | | * Example of use: |
| | | * \code |
| | | * Json::Value object; |
| | | * static const StaticString code("code"); |
| | | * object[code] = 1234; |
| | | * \endcode |
| | | */ |
| | | Value &operator[]( const StaticString &key ); |
| | | # ifdef JSON_USE_CPPTL |
| | | /// Access an object value by name, create a null member if it does not exist. |
| | | Value &operator[]( const CppTL::ConstString &key ); |
| | | /// Access an object value by name, returns null if there is no member with that name. |
| | | const Value &operator[]( const CppTL::ConstString &key ) const; |
| | | # endif |
| | | /// Return the member named key if it exist, defaultValue otherwise. |
| | | Value get( const char *key, |
| | | const Value &defaultValue ) const; |
| | | /// Return the member named key if it exist, defaultValue otherwise. |
| | | Value get( const std::string &key, |
| | | const Value &defaultValue ) const; |
| | | # ifdef JSON_USE_CPPTL |
| | | /// Return the member named key if it exist, defaultValue otherwise. |
| | | Value get( const CppTL::ConstString &key, |
| | | const Value &defaultValue ) const; |
| | | # endif |
| | | /// \brief Remove and return the named member. |
| | | /// |
| | | /// Do nothing if it did not exist. |
| | | /// \return the removed Value, or null. |
| | | /// \pre type() is objectValue or nullValue |
| | | /// \post type() is unchanged |
| | | Value removeMember( const char* key ); |
| | | /// Same as removeMember(const char*) |
| | | Value removeMember( const std::string &key ); |
| | | |
| | | /// Return true if the object has a member named key. |
| | | bool isMember( const char *key ) const; |
| | | /// Return true if the object has a member named key. |
| | | bool isMember( const std::string &key ) const; |
| | | # ifdef JSON_USE_CPPTL |
| | | /// Return true if the object has a member named key. |
| | | bool isMember( const CppTL::ConstString &key ) const; |
| | | # endif |
| | | |
| | | /// \brief Return a list of the member names. |
| | | /// |
| | | /// If null, return an empty list. |
| | | /// \pre type() is objectValue or nullValue |
| | | /// \post if type() was nullValue, it remains nullValue |
| | | Members getMemberNames() const; |
| | | |
| | | //# ifdef JSON_USE_CPPTL |
| | | // EnumMemberNames enumMemberNames() const; |
| | | // EnumValues enumValues() const; |
| | | //# endif |
| | | |
| | | /// Comments must be //... or /* ... */ |
| | | void setComment( const char *comment, |
| | | CommentPlacement placement ); |
| | | /// Comments must be //... or /* ... */ |
| | | void setComment( const std::string &comment, |
| | | CommentPlacement placement ); |
| | | bool hasComment( CommentPlacement placement ) const; |
| | | /// Include delimiters and embedded newlines. |
| | | std::string getComment( CommentPlacement placement ) const; |
| | | |
| | | std::string toStyledString() const; |
| | | |
| | | const_iterator begin() const; |
| | | const_iterator end() const; |
| | | |
| | | iterator begin(); |
| | | iterator end(); |
| | | |
| | | private: |
| | | Value &resolveReference( const char *key, |
| | | bool isStatic ); |
| | | |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | inline bool isItemAvailable() const |
| | | { |
| | | return itemIsUsed_ == 0; |
| | | } |
| | | |
| | | inline void setItemUsed( bool isUsed = true ) |
| | | { |
| | | itemIsUsed_ = isUsed ? 1 : 0; |
| | | } |
| | | |
| | | inline bool isMemberNameStatic() const |
| | | { |
| | | return memberNameIsStatic_ == 0; |
| | | } |
| | | |
| | | inline void setMemberNameIsStatic( bool isStatic ) |
| | | { |
| | | memberNameIsStatic_ = isStatic ? 1 : 0; |
| | | } |
| | | # endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | |
| | | private: |
| | | struct CommentInfo |
| | | { |
| | | CommentInfo(); |
| | | ~CommentInfo(); |
| | | |
| | | void setComment( const char *text ); |
| | | |
| | | char *comment_; |
| | | }; |
| | | |
| | | //struct MemberNamesTransform |
| | | //{ |
| | | // typedef const char *result_type; |
| | | // const char *operator()( const CZString &name ) const |
| | | // { |
| | | // return name.c_str(); |
| | | // } |
| | | //}; |
| | | |
| | | union ValueHolder |
| | | { |
| | | Int int_; |
| | | UInt uint_; |
| | | double real_; |
| | | bool bool_; |
| | | char *string_; |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | ValueInternalArray *array_; |
| | | ValueInternalMap *map_; |
| | | #else |
| | | ObjectValues *map_; |
| | | # endif |
| | | } value_; |
| | | ValueType type_ : 8; |
| | | int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. |
| | | int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. |
| | | # endif |
| | | CommentInfo *comments_; |
| | | }; |
| | | |
| | | |
| | | /** \brief Experimental and untested: represents an element of the "path" to access a node. |
| | | */ |
| | | class PathArgument |
| | | { |
| | | public: |
| | | friend class Path; |
| | | |
| | | PathArgument(); |
| | | PathArgument( UInt index ); |
| | | PathArgument( const char *key ); |
| | | PathArgument( const std::string &key ); |
| | | |
| | | private: |
| | | enum Kind |
| | | { |
| | | kindNone = 0, |
| | | kindIndex, |
| | | kindKey |
| | | }; |
| | | std::string key_; |
| | | UInt index_; |
| | | Kind kind_; |
| | | }; |
| | | |
| | | /** \brief Experimental and untested: represents a "path" to access a node. |
| | | * |
| | | * Syntax: |
| | | * - "." => root node |
| | | * - ".[n]" => elements at index 'n' of root node (an array value) |
| | | * - ".name" => member named 'name' of root node (an object value) |
| | | * - ".name1.name2.name3" |
| | | * - ".[0][1][2].name1[3]" |
| | | * - ".%" => member name is provided as parameter |
| | | * - ".[%]" => index is provied as parameter |
| | | */ |
| | | class Path |
| | | { |
| | | public: |
| | | Path( const std::string &path, |
| | | const PathArgument &a1 = PathArgument(), |
| | | const PathArgument &a2 = PathArgument(), |
| | | const PathArgument &a3 = PathArgument(), |
| | | const PathArgument &a4 = PathArgument(), |
| | | const PathArgument &a5 = PathArgument() ); |
| | | |
| | | const Value &resolve( const Value &root ) const; |
| | | Value resolve( const Value &root, |
| | | const Value &defaultValue ) const; |
| | | /// Creates the "path" to access the specified node and returns a reference on the node. |
| | | Value &make( Value &root ) const; |
| | | |
| | | private: |
| | | typedef std::vector<const PathArgument *> InArgs; |
| | | typedef std::vector<PathArgument> Args; |
| | | |
| | | void makePath( const std::string &path, |
| | | const InArgs &in ); |
| | | void addPathInArg( const std::string &path, |
| | | const InArgs &in, |
| | | InArgs::const_iterator &itInArg, |
| | | PathArgument::Kind kind ); |
| | | void invalidPath( const std::string &path, |
| | | int location ); |
| | | |
| | | Args args_; |
| | | }; |
| | | |
| | | /** \brief Experimental do not use: Allocator to customize member name and string value memory management done by Value. |
| | | * |
| | | * - makeMemberName() and releaseMemberName() are called to respectively duplicate and |
| | | * free an Json::objectValue member name. |
| | | * - duplicateStringValue() and releaseStringValue() are called similarly to |
| | | * duplicate and free a Json::stringValue value. |
| | | */ |
| | | class ValueAllocator |
| | | { |
| | | public: |
| | | enum { unknown = (unsigned)-1 }; |
| | | |
| | | virtual ~ValueAllocator(); |
| | | |
| | | virtual char *makeMemberName( const char *memberName ) = 0; |
| | | virtual void releaseMemberName( char *memberName ) = 0; |
| | | virtual char *duplicateStringValue( const char *value, |
| | | unsigned int length = unknown ) = 0; |
| | | virtual void releaseStringValue( char *value ) = 0; |
| | | }; |
| | | |
| | | #ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | /** \brief Allocator to customize Value internal map. |
| | | * Below is an example of a simple implementation (default implementation actually |
| | | * use memory pool for speed). |
| | | * \code |
| | | class DefaultValueMapAllocator : public ValueMapAllocator |
| | | { |
| | | public: // overridden from ValueMapAllocator |
| | | virtual ValueInternalMap *newMap() |
| | | { |
| | | return new ValueInternalMap(); |
| | | } |
| | | |
| | | virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) |
| | | { |
| | | return new ValueInternalMap( other ); |
| | | } |
| | | |
| | | virtual void destructMap( ValueInternalMap *map ) |
| | | { |
| | | delete map; |
| | | } |
| | | |
| | | virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) |
| | | { |
| | | return new ValueInternalLink[size]; |
| | | } |
| | | |
| | | virtual void releaseMapBuckets( ValueInternalLink *links ) |
| | | { |
| | | delete [] links; |
| | | } |
| | | |
| | | virtual ValueInternalLink *allocateMapLink() |
| | | { |
| | | return new ValueInternalLink(); |
| | | } |
| | | |
| | | virtual void releaseMapLink( ValueInternalLink *link ) |
| | | { |
| | | delete link; |
| | | } |
| | | }; |
| | | * \endcode |
| | | */ |
| | | class JSON_API ValueMapAllocator |
| | | { |
| | | public: |
| | | virtual ~ValueMapAllocator(); |
| | | virtual ValueInternalMap *newMap() = 0; |
| | | virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0; |
| | | virtual void destructMap( ValueInternalMap *map ) = 0; |
| | | virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0; |
| | | virtual void releaseMapBuckets( ValueInternalLink *links ) = 0; |
| | | virtual ValueInternalLink *allocateMapLink() = 0; |
| | | virtual void releaseMapLink( ValueInternalLink *link ) = 0; |
| | | }; |
| | | |
| | | /** \brief ValueInternalMap hash-map bucket chain link (for internal use only). |
| | | * \internal previous_ & next_ allows for bidirectional traversal. |
| | | */ |
| | | class JSON_API ValueInternalLink |
| | | { |
| | | public: |
| | | enum { itemPerLink = 6 }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. |
| | | enum InternalFlags { |
| | | flagAvailable = 0, |
| | | flagUsed = 1 |
| | | }; |
| | | |
| | | ValueInternalLink(); |
| | | |
| | | ~ValueInternalLink(); |
| | | |
| | | Value items_[itemPerLink]; |
| | | char *keys_[itemPerLink]; |
| | | ValueInternalLink *previous_; |
| | | ValueInternalLink *next_; |
| | | }; |
| | | |
| | | |
| | | /** \brief A linked page based hash-table implementation used internally by Value. |
| | | * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked |
| | | * list in each bucket to handle collision. There is an addional twist in that |
| | | * each node of the collision linked list is a page containing a fixed amount of |
| | | * value. This provides a better compromise between memory usage and speed. |
| | | * |
| | | * Each bucket is made up of a chained list of ValueInternalLink. The last |
| | | * link of a given bucket can be found in the 'previous_' field of the following bucket. |
| | | * The last link of the last bucket is stored in tailLink_ as it has no following bucket. |
| | | * Only the last link of a bucket may contains 'available' item. The last link always |
| | | * contains at least one element unless is it the bucket one very first link. |
| | | */ |
| | | class JSON_API ValueInternalMap |
| | | { |
| | | friend class ValueIteratorBase; |
| | | friend class Value; |
| | | public: |
| | | typedef unsigned int HashKey; |
| | | typedef unsigned int BucketIndex; |
| | | |
| | | # ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION |
| | | struct IteratorState |
| | | { |
| | | IteratorState() |
| | | : map_(0) |
| | | , link_(0) |
| | | , itemIndex_(0) |
| | | , bucketIndex_(0) |
| | | { |
| | | } |
| | | ValueInternalMap *map_; |
| | | ValueInternalLink *link_; |
| | | BucketIndex itemIndex_; |
| | | BucketIndex bucketIndex_; |
| | | }; |
| | | # endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION |
| | | |
| | | ValueInternalMap(); |
| | | ValueInternalMap( const ValueInternalMap &other ); |
| | | ValueInternalMap &operator =( const ValueInternalMap &other ); |
| | | ~ValueInternalMap(); |
| | | |
| | | void swap( ValueInternalMap &other ); |
| | | |
| | | BucketIndex size() const; |
| | | |
| | | void clear(); |
| | | |
| | | bool reserveDelta( BucketIndex growth ); |
| | | |
| | | bool reserve( BucketIndex newItemCount ); |
| | | |
| | | const Value *find( const char *key ) const; |
| | | |
| | | Value *find( const char *key ); |
| | | |
| | | Value &resolveReference( const char *key, |
| | | bool isStatic ); |
| | | |
| | | void remove( const char *key ); |
| | | |
| | | void doActualRemove( ValueInternalLink *link, |
| | | BucketIndex index, |
| | | BucketIndex bucketIndex ); |
| | | |
| | | ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex ); |
| | | |
| | | Value &setNewItem( const char *key, |
| | | bool isStatic, |
| | | ValueInternalLink *link, |
| | | BucketIndex index ); |
| | | |
| | | Value &unsafeAdd( const char *key, |
| | | bool isStatic, |
| | | HashKey hashedKey ); |
| | | |
| | | HashKey hash( const char *key ) const; |
| | | |
| | | int compare( const ValueInternalMap &other ) const; |
| | | |
| | | private: |
| | | void makeBeginIterator( IteratorState &it ) const; |
| | | void makeEndIterator( IteratorState &it ) const; |
| | | static bool equals( const IteratorState &x, const IteratorState &other ); |
| | | static void increment( IteratorState &iterator ); |
| | | static void incrementBucket( IteratorState &iterator ); |
| | | static void decrement( IteratorState &iterator ); |
| | | static const char *key( const IteratorState &iterator ); |
| | | static const char *key( const IteratorState &iterator, bool &isStatic ); |
| | | static Value &value( const IteratorState &iterator ); |
| | | static int distance( const IteratorState &x, const IteratorState &y ); |
| | | |
| | | private: |
| | | ValueInternalLink *buckets_; |
| | | ValueInternalLink *tailLink_; |
| | | BucketIndex bucketsSize_; |
| | | BucketIndex itemCount_; |
| | | }; |
| | | |
| | | /** \brief A simplified deque implementation used internally by Value. |
| | | * \internal |
| | | * It is based on a list of fixed "page", each page contains a fixed number of items. |
| | | * Instead of using a linked-list, a array of pointer is used for fast item look-up. |
| | | * Look-up for an element is as follow: |
| | | * - compute page index: pageIndex = itemIndex / itemsPerPage |
| | | * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] |
| | | * |
| | | * Insertion is amortized constant time (only the array containing the index of pointers |
| | | * need to be reallocated when items are appended). |
| | | */ |
| | | class JSON_API ValueInternalArray |
| | | { |
| | | friend class Value; |
| | | friend class ValueIteratorBase; |
| | | public: |
| | | enum { itemsPerPage = 8 }; // should be a power of 2 for fast divide and modulo. |
| | | typedef Value::ArrayIndex ArrayIndex; |
| | | typedef unsigned int PageIndex; |
| | | |
| | | # ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION |
| | | struct IteratorState // Must be a POD |
| | | { |
| | | IteratorState() |
| | | : array_(0) |
| | | , currentPageIndex_(0) |
| | | , currentItemIndex_(0) |
| | | { |
| | | } |
| | | ValueInternalArray *array_; |
| | | Value **currentPageIndex_; |
| | | unsigned int currentItemIndex_; |
| | | }; |
| | | # endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION |
| | | |
| | | ValueInternalArray(); |
| | | ValueInternalArray( const ValueInternalArray &other ); |
| | | ValueInternalArray &operator =( const ValueInternalArray &other ); |
| | | ~ValueInternalArray(); |
| | | void swap( ValueInternalArray &other ); |
| | | |
| | | void clear(); |
| | | void resize( ArrayIndex newSize ); |
| | | |
| | | Value &resolveReference( ArrayIndex index ); |
| | | |
| | | Value *find( ArrayIndex index ) const; |
| | | |
| | | ArrayIndex size() const; |
| | | |
| | | int compare( const ValueInternalArray &other ) const; |
| | | |
| | | private: |
| | | static bool equals( const IteratorState &x, const IteratorState &other ); |
| | | static void increment( IteratorState &iterator ); |
| | | static void decrement( IteratorState &iterator ); |
| | | static Value &dereference( const IteratorState &iterator ); |
| | | static Value &unsafeDereference( const IteratorState &iterator ); |
| | | static int distance( const IteratorState &x, const IteratorState &y ); |
| | | static ArrayIndex indexOf( const IteratorState &iterator ); |
| | | void makeBeginIterator( IteratorState &it ) const; |
| | | void makeEndIterator( IteratorState &it ) const; |
| | | void makeIterator( IteratorState &it, ArrayIndex index ) const; |
| | | |
| | | void makeIndexValid( ArrayIndex index ); |
| | | |
| | | Value **pages_; |
| | | ArrayIndex size_; |
| | | PageIndex pageCount_; |
| | | }; |
| | | |
| | | /** \brief Experimental: do not use. Allocator to customize Value internal array. |
| | | * Below is an example of a simple implementation (actual implementation use |
| | | * memory pool). |
| | | \code |
| | | class DefaultValueArrayAllocator : public ValueArrayAllocator |
| | | { |
| | | public: // overridden from ValueArrayAllocator |
| | | virtual ~DefaultValueArrayAllocator() |
| | | { |
| | | } |
| | | |
| | | virtual ValueInternalArray *newArray() |
| | | { |
| | | return new ValueInternalArray(); |
| | | } |
| | | |
| | | virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) |
| | | { |
| | | return new ValueInternalArray( other ); |
| | | } |
| | | |
| | | virtual void destruct( ValueInternalArray *array ) |
| | | { |
| | | delete array; |
| | | } |
| | | |
| | | virtual void reallocateArrayPageIndex( Value **&indexes, |
| | | ValueInternalArray::PageIndex &indexCount, |
| | | ValueInternalArray::PageIndex minNewIndexCount ) |
| | | { |
| | | ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; |
| | | if ( minNewIndexCount > newIndexCount ) |
| | | newIndexCount = minNewIndexCount; |
| | | void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); |
| | | if ( !newIndexes ) |
| | | throw std::bad_alloc(); |
| | | indexCount = newIndexCount; |
| | | indexes = static_cast<Value **>( newIndexes ); |
| | | } |
| | | virtual void releaseArrayPageIndex( Value **indexes, |
| | | ValueInternalArray::PageIndex indexCount ) |
| | | { |
| | | if ( indexes ) |
| | | free( indexes ); |
| | | } |
| | | |
| | | virtual Value *allocateArrayPage() |
| | | { |
| | | return static_cast<Value *>( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); |
| | | } |
| | | |
| | | virtual void releaseArrayPage( Value *value ) |
| | | { |
| | | if ( value ) |
| | | free( value ); |
| | | } |
| | | }; |
| | | \endcode |
| | | */ |
| | | class JSON_API ValueArrayAllocator |
| | | { |
| | | public: |
| | | virtual ~ValueArrayAllocator(); |
| | | virtual ValueInternalArray *newArray() = 0; |
| | | virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0; |
| | | virtual void destructArray( ValueInternalArray *array ) = 0; |
| | | /** \brief Reallocate array page index. |
| | | * Reallocates an array of pointer on each page. |
| | | * \param indexes [input] pointer on the current index. May be \c NULL. |
| | | * [output] pointer on the new index of at least |
| | | * \a minNewIndexCount pages. |
| | | * \param indexCount [input] current number of pages in the index. |
| | | * [output] number of page the reallocated index can handle. |
| | | * \b MUST be >= \a minNewIndexCount. |
| | | * \param minNewIndexCount Minimum number of page the new index must be able to |
| | | * handle. |
| | | */ |
| | | virtual void reallocateArrayPageIndex( Value **&indexes, |
| | | ValueInternalArray::PageIndex &indexCount, |
| | | ValueInternalArray::PageIndex minNewIndexCount ) = 0; |
| | | virtual void releaseArrayPageIndex( Value **indexes, |
| | | ValueInternalArray::PageIndex indexCount ) = 0; |
| | | virtual Value *allocateArrayPage() = 0; |
| | | virtual void releaseArrayPage( Value *value ) = 0; |
| | | }; |
| | | #endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | |
| | | |
| | | /** \brief base class for Value iterators. |
| | | * |
| | | */ |
| | | class ValueIteratorBase |
| | | { |
| | | public: |
| | | typedef unsigned int size_t; |
| | | typedef int difference_type; |
| | | typedef ValueIteratorBase SelfType; |
| | | |
| | | ValueIteratorBase(); |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | explicit ValueIteratorBase( const Value::ObjectValues::iterator ¤t ); |
| | | #else |
| | | ValueIteratorBase( const ValueInternalArray::IteratorState &state ); |
| | | ValueIteratorBase( const ValueInternalMap::IteratorState &state ); |
| | | #endif |
| | | |
| | | bool operator ==( const SelfType &other ) const |
| | | { |
| | | return isEqual( other ); |
| | | } |
| | | |
| | | bool operator !=( const SelfType &other ) const |
| | | { |
| | | return !isEqual( other ); |
| | | } |
| | | |
| | | difference_type operator -( const SelfType &other ) const |
| | | { |
| | | return computeDistance( other ); |
| | | } |
| | | |
| | | /// Return either the index or the member name of the referenced value as a Value. |
| | | Value key() const; |
| | | |
| | | /// Return the index of the referenced Value. -1 if it is not an arrayValue. |
| | | UInt index() const; |
| | | |
| | | /// Return the member name of the referenced Value. "" if it is not an objectValue. |
| | | const char *memberName() const; |
| | | |
| | | protected: |
| | | Value &deref() const; |
| | | |
| | | void increment(); |
| | | |
| | | void decrement(); |
| | | |
| | | difference_type computeDistance( const SelfType &other ) const; |
| | | |
| | | bool isEqual( const SelfType &other ) const; |
| | | |
| | | void copy( const SelfType &other ); |
| | | |
| | | private: |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | Value::ObjectValues::iterator current_; |
| | | // Indicates that iterator is for a null value. |
| | | bool isNull_; |
| | | #else |
| | | union |
| | | { |
| | | ValueInternalArray::IteratorState array_; |
| | | ValueInternalMap::IteratorState map_; |
| | | } iterator_; |
| | | bool isArray_; |
| | | #endif |
| | | }; |
| | | |
| | | /** \brief const iterator for object and array value. |
| | | * |
| | | */ |
| | | class ValueConstIterator : public ValueIteratorBase |
| | | { |
| | | friend class Value; |
| | | public: |
| | | typedef unsigned int size_t; |
| | | typedef int difference_type; |
| | | typedef const Value &reference; |
| | | typedef const Value *pointer; |
| | | typedef ValueConstIterator SelfType; |
| | | |
| | | ValueConstIterator(); |
| | | private: |
| | | /*! \internal Use by Value to create an iterator. |
| | | */ |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | explicit ValueConstIterator( const Value::ObjectValues::iterator ¤t ); |
| | | #else |
| | | ValueConstIterator( const ValueInternalArray::IteratorState &state ); |
| | | ValueConstIterator( const ValueInternalMap::IteratorState &state ); |
| | | #endif |
| | | public: |
| | | SelfType &operator =( const ValueIteratorBase &other ); |
| | | |
| | | SelfType operator++( int ) |
| | | { |
| | | SelfType temp( *this ); |
| | | ++*this; |
| | | return temp; |
| | | } |
| | | |
| | | SelfType operator--( int ) |
| | | { |
| | | SelfType temp( *this ); |
| | | --*this; |
| | | return temp; |
| | | } |
| | | |
| | | SelfType &operator--() |
| | | { |
| | | decrement(); |
| | | return *this; |
| | | } |
| | | |
| | | SelfType &operator++() |
| | | { |
| | | increment(); |
| | | return *this; |
| | | } |
| | | |
| | | reference operator *() const |
| | | { |
| | | return deref(); |
| | | } |
| | | }; |
| | | |
| | | |
| | | /** \brief Iterator for object and array value. |
| | | */ |
| | | class ValueIterator : public ValueIteratorBase |
| | | { |
| | | friend class Value; |
| | | public: |
| | | typedef unsigned int size_t; |
| | | typedef int difference_type; |
| | | typedef Value &reference; |
| | | typedef Value *pointer; |
| | | typedef ValueIterator SelfType; |
| | | |
| | | ValueIterator(); |
| | | ValueIterator( const ValueConstIterator &other ); |
| | | ValueIterator( const ValueIterator &other ); |
| | | private: |
| | | /*! \internal Use by Value to create an iterator. |
| | | */ |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | explicit ValueIterator( const Value::ObjectValues::iterator ¤t ); |
| | | #else |
| | | ValueIterator( const ValueInternalArray::IteratorState &state ); |
| | | ValueIterator( const ValueInternalMap::IteratorState &state ); |
| | | #endif |
| | | public: |
| | | |
| | | SelfType &operator =( const SelfType &other ); |
| | | |
| | | SelfType operator++( int ) |
| | | { |
| | | SelfType temp( *this ); |
| | | ++*this; |
| | | return temp; |
| | | } |
| | | |
| | | SelfType operator--( int ) |
| | | { |
| | | SelfType temp( *this ); |
| | | --*this; |
| | | return temp; |
| | | } |
| | | |
| | | SelfType &operator--() |
| | | { |
| | | decrement(); |
| | | return *this; |
| | | } |
| | | |
| | | SelfType &operator++() |
| | | { |
| | | increment(); |
| | | return *this; |
| | | } |
| | | |
| | | reference operator *() const |
| | | { |
| | | return deref(); |
| | | } |
| | | }; |
| | | |
| | | |
| | | } // namespace Json |
| | | |
| | | |
| | | #endif // CPPTL_JSON_H_INCLUDED |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #ifndef JSON_WRITER_H_INCLUDED |
| | | # define JSON_WRITER_H_INCLUDED |
| | | |
| | | # include "value.h" |
| | | # include <vector> |
| | | # include <string> |
| | | # include <iostream> |
| | | |
| | | namespace Json { |
| | | |
| | | class Value; |
| | | |
| | | /** \brief Abstract class for writers. |
| | | */ |
| | | class JSON_API Writer |
| | | { |
| | | public: |
| | | virtual ~Writer(); |
| | | |
| | | virtual std::string write( const Value &root ) = 0; |
| | | }; |
| | | |
| | | /** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format without formatting (not human friendly). |
| | | * |
| | | * The JSON document is written in a single line. It is not intended for 'human' consumption, |
| | | * but may be usefull to support feature such as RPC where bandwith is limited. |
| | | * \sa Reader, Value |
| | | */ |
| | | class JSON_API FastWriter : public Writer |
| | | { |
| | | public: |
| | | FastWriter(); |
| | | virtual ~FastWriter(){} |
| | | |
| | | void enableYAMLCompatibility(); |
| | | |
| | | public: // overridden from Writer |
| | | virtual std::string write( const Value &root ); |
| | | |
| | | private: |
| | | void writeValue( const Value &value ); |
| | | |
| | | std::string document_; |
| | | bool yamlCompatiblityEnabled_; |
| | | }; |
| | | |
| | | /** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way. |
| | | * |
| | | * The rules for line break and indent are as follow: |
| | | * - Object value: |
| | | * - if empty then print {} without indent and line break |
| | | * - if not empty the print '{', line break & indent, print one value per line |
| | | * and then unindent and line break and print '}'. |
| | | * - Array value: |
| | | * - if empty then print [] without indent and line break |
| | | * - if the array contains no object value, empty array or some other value types, |
| | | * and all the values fit on one lines, then print the array on a single line. |
| | | * - otherwise, it the values do not fit on one line, or the array contains |
| | | * object or non empty array, then print one value per line. |
| | | * |
| | | * If the Value have comments then they are outputed according to their #CommentPlacement. |
| | | * |
| | | * \sa Reader, Value, Value::setComment() |
| | | */ |
| | | class JSON_API StyledWriter: public Writer |
| | | { |
| | | public: |
| | | StyledWriter(); |
| | | virtual ~StyledWriter(){} |
| | | |
| | | public: // overridden from Writer |
| | | /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format. |
| | | * \param root Value to serialize. |
| | | * \return String containing the JSON document that represents the root value. |
| | | */ |
| | | virtual std::string write( const Value &root ); |
| | | |
| | | private: |
| | | void writeValue( const Value &value ); |
| | | void writeArrayValue( const Value &value ); |
| | | bool isMultineArray( const Value &value ); |
| | | void pushValue( const std::string &value ); |
| | | void writeIndent(); |
| | | void writeWithIndent( const std::string &value ); |
| | | void indent(); |
| | | void unindent(); |
| | | void writeCommentBeforeValue( const Value &root ); |
| | | void writeCommentAfterValueOnSameLine( const Value &root ); |
| | | bool hasCommentForValue( const Value &value ); |
| | | static std::string normalizeEOL( const std::string &text ); |
| | | |
| | | typedef std::vector<std::string> ChildValues; |
| | | |
| | | ChildValues childValues_; |
| | | std::string document_; |
| | | std::string indentString_; |
| | | int rightMargin_; |
| | | int indentSize_; |
| | | bool addChildValues_; |
| | | }; |
| | | |
| | | /** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way, |
| | | to a stream rather than to a string. |
| | | * |
| | | * The rules for line break and indent are as follow: |
| | | * - Object value: |
| | | * - if empty then print {} without indent and line break |
| | | * - if not empty the print '{', line break & indent, print one value per line |
| | | * and then unindent and line break and print '}'. |
| | | * - Array value: |
| | | * - if empty then print [] without indent and line break |
| | | * - if the array contains no object value, empty array or some other value types, |
| | | * and all the values fit on one lines, then print the array on a single line. |
| | | * - otherwise, it the values do not fit on one line, or the array contains |
| | | * object or non empty array, then print one value per line. |
| | | * |
| | | * If the Value have comments then they are outputed according to their #CommentPlacement. |
| | | * |
| | | * \param indentation Each level will be indented by this amount extra. |
| | | * \sa Reader, Value, Value::setComment() |
| | | */ |
| | | class JSON_API StyledStreamWriter |
| | | { |
| | | public: |
| | | StyledStreamWriter( std::string indentation="\t" ); |
| | | ~StyledStreamWriter(){} |
| | | |
| | | public: |
| | | /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format. |
| | | * \param out Stream to write to. (Can be ostringstream, e.g.) |
| | | * \param root Value to serialize. |
| | | * \note There is no point in deriving from Writer, since write() should not return a value. |
| | | */ |
| | | void write( std::ostream &out, const Value &root ); |
| | | |
| | | private: |
| | | void writeValue( const Value &value ); |
| | | void writeArrayValue( const Value &value ); |
| | | bool isMultineArray( const Value &value ); |
| | | void pushValue( const std::string &value ); |
| | | void writeIndent(); |
| | | void writeWithIndent( const std::string &value ); |
| | | void indent(); |
| | | void unindent(); |
| | | void writeCommentBeforeValue( const Value &root ); |
| | | void writeCommentAfterValueOnSameLine( const Value &root ); |
| | | bool hasCommentForValue( const Value &value ); |
| | | static std::string normalizeEOL( const std::string &text ); |
| | | |
| | | typedef std::vector<std::string> ChildValues; |
| | | |
| | | ChildValues childValues_; |
| | | std::ostream* document_; |
| | | std::string indentString_; |
| | | int rightMargin_; |
| | | std::string indentation_; |
| | | bool addChildValues_; |
| | | }; |
| | | |
| | | std::string JSON_API valueToString( Int value ); |
| | | std::string JSON_API valueToString( UInt value ); |
| | | std::string JSON_API valueToString( double value ); |
| | | std::string JSON_API valueToString( bool value ); |
| | | std::string JSON_API valueToQuotedString( const char *value ); |
| | | |
| | | /// \brief Output using the StyledStreamWriter. |
| | | /// \see Json::operator>>() |
| | | std::ostream& operator<<( std::ostream&, const Value &root ); |
| | | |
| | | } // namespace Json |
| | | |
| | | |
| | | |
| | | #endif // JSON_WRITER_H_INCLUDED |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED |
| | | # define JSONCPP_BATCHALLOCATOR_H_INCLUDED |
| | | |
| | | # include <stdlib.h> |
| | | # include <assert.h> |
| | | |
| | | # ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION |
| | | |
| | | namespace Json { |
| | | |
| | | /* Fast memory allocator. |
| | | * |
| | | * This memory allocator allocates memory for a batch of object (specified by |
| | | * the page size, the number of object in each page). |
| | | * |
| | | * It does not allow the destruction of a single object. All the allocated objects |
| | | * can be destroyed at once. The memory can be either released or reused for future |
| | | * allocation. |
| | | * |
| | | * The in-place new operator must be used to construct the object using the pointer |
| | | * returned by allocate. |
| | | */ |
| | | template<typename AllocatedType |
| | | ,const unsigned int objectPerAllocation> |
| | | class BatchAllocator |
| | | { |
| | | public: |
| | | typedef AllocatedType Type; |
| | | |
| | | BatchAllocator( unsigned int objectsPerPage = 255 ) |
| | | : freeHead_( 0 ) |
| | | , objectsPerPage_( objectsPerPage ) |
| | | { |
| | | // printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); |
| | | assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space. |
| | | assert( objectsPerPage >= 16 ); |
| | | batches_ = allocateBatch( 0 ); // allocated a dummy page |
| | | currentBatch_ = batches_; |
| | | } |
| | | |
| | | ~BatchAllocator() |
| | | { |
| | | for ( BatchInfo *batch = batches_; batch; ) |
| | | { |
| | | BatchInfo *nextBatch = batch->next_; |
| | | free( batch ); |
| | | batch = nextBatch; |
| | | } |
| | | } |
| | | |
| | | /// allocate space for an array of objectPerAllocation object. |
| | | /// @warning it is the responsability of the caller to call objects constructors. |
| | | AllocatedType *allocate() |
| | | { |
| | | if ( freeHead_ ) // returns node from free list. |
| | | { |
| | | AllocatedType *object = freeHead_; |
| | | freeHead_ = *(AllocatedType **)object; |
| | | return object; |
| | | } |
| | | if ( currentBatch_->used_ == currentBatch_->end_ ) |
| | | { |
| | | currentBatch_ = currentBatch_->next_; |
| | | while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ ) |
| | | currentBatch_ = currentBatch_->next_; |
| | | |
| | | if ( !currentBatch_ ) // no free batch found, allocate a new one |
| | | { |
| | | currentBatch_ = allocateBatch( objectsPerPage_ ); |
| | | currentBatch_->next_ = batches_; // insert at the head of the list |
| | | batches_ = currentBatch_; |
| | | } |
| | | } |
| | | AllocatedType *allocated = currentBatch_->used_; |
| | | currentBatch_->used_ += objectPerAllocation; |
| | | return allocated; |
| | | } |
| | | |
| | | /// Release the object. |
| | | /// @warning it is the responsability of the caller to actually destruct the object. |
| | | void release( AllocatedType *object ) |
| | | { |
| | | assert( object != 0 ); |
| | | *(AllocatedType **)object = freeHead_; |
| | | freeHead_ = object; |
| | | } |
| | | |
| | | private: |
| | | struct BatchInfo |
| | | { |
| | | BatchInfo *next_; |
| | | AllocatedType *used_; |
| | | AllocatedType *end_; |
| | | AllocatedType buffer_[objectPerAllocation]; |
| | | }; |
| | | |
| | | // disabled copy constructor and assignement operator. |
| | | BatchAllocator( const BatchAllocator & ); |
| | | void operator =( const BatchAllocator &); |
| | | |
| | | static BatchInfo *allocateBatch( unsigned int objectsPerPage ) |
| | | { |
| | | const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation |
| | | + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; |
| | | BatchInfo *batch = static_cast<BatchInfo*>( malloc( mallocSize ) ); |
| | | batch->next_ = 0; |
| | | batch->used_ = batch->buffer_; |
| | | batch->end_ = batch->buffer_ + objectsPerPage; |
| | | return batch; |
| | | } |
| | | |
| | | BatchInfo *batches_; |
| | | BatchInfo *currentBatch_; |
| | | /// Head of a single linked list within the allocated space of freeed object |
| | | AllocatedType *freeHead_; |
| | | unsigned int objectsPerPage_; |
| | | }; |
| | | |
| | | |
| | | } // namespace Json |
| | | |
| | | # endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION |
| | | |
| | | #endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | // included by json_value.cpp |
| | | // everything is within Json namespace |
| | | |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // class ValueInternalArray |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | |
| | | ValueArrayAllocator::~ValueArrayAllocator() |
| | | { |
| | | } |
| | | |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // class DefaultValueArrayAllocator |
| | | // ////////////////////////////////////////////////////////////////// |
| | | #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR |
| | | class DefaultValueArrayAllocator : public ValueArrayAllocator |
| | | { |
| | | public: // overridden from ValueArrayAllocator |
| | | virtual ~DefaultValueArrayAllocator() |
| | | { |
| | | } |
| | | |
| | | virtual ValueInternalArray *newArray() |
| | | { |
| | | return new ValueInternalArray(); |
| | | } |
| | | |
| | | virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) |
| | | { |
| | | return new ValueInternalArray( other ); |
| | | } |
| | | |
| | | virtual void destructArray( ValueInternalArray *array ) |
| | | { |
| | | delete array; |
| | | } |
| | | |
| | | virtual void reallocateArrayPageIndex( Value **&indexes, |
| | | ValueInternalArray::PageIndex &indexCount, |
| | | ValueInternalArray::PageIndex minNewIndexCount ) |
| | | { |
| | | ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; |
| | | if ( minNewIndexCount > newIndexCount ) |
| | | newIndexCount = minNewIndexCount; |
| | | void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); |
| | | if ( !newIndexes ) |
| | | throw std::bad_alloc(); |
| | | indexCount = newIndexCount; |
| | | indexes = static_cast<Value **>( newIndexes ); |
| | | } |
| | | virtual void releaseArrayPageIndex( Value **indexes, |
| | | ValueInternalArray::PageIndex indexCount ) |
| | | { |
| | | if ( indexes ) |
| | | free( indexes ); |
| | | } |
| | | |
| | | virtual Value *allocateArrayPage() |
| | | { |
| | | return static_cast<Value *>( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); |
| | | } |
| | | |
| | | virtual void releaseArrayPage( Value *value ) |
| | | { |
| | | if ( value ) |
| | | free( value ); |
| | | } |
| | | }; |
| | | |
| | | #else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR |
| | | /// @todo make this thread-safe (lock when accessign batch allocator) |
| | | class DefaultValueArrayAllocator : public ValueArrayAllocator |
| | | { |
| | | public: // overridden from ValueArrayAllocator |
| | | virtual ~DefaultValueArrayAllocator() |
| | | { |
| | | } |
| | | |
| | | virtual ValueInternalArray *newArray() |
| | | { |
| | | ValueInternalArray *array = arraysAllocator_.allocate(); |
| | | new (array) ValueInternalArray(); // placement new |
| | | return array; |
| | | } |
| | | |
| | | virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) |
| | | { |
| | | ValueInternalArray *array = arraysAllocator_.allocate(); |
| | | new (array) ValueInternalArray( other ); // placement new |
| | | return array; |
| | | } |
| | | |
| | | virtual void destructArray( ValueInternalArray *array ) |
| | | { |
| | | if ( array ) |
| | | { |
| | | array->~ValueInternalArray(); |
| | | arraysAllocator_.release( array ); |
| | | } |
| | | } |
| | | |
| | | virtual void reallocateArrayPageIndex( Value **&indexes, |
| | | ValueInternalArray::PageIndex &indexCount, |
| | | ValueInternalArray::PageIndex minNewIndexCount ) |
| | | { |
| | | ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; |
| | | if ( minNewIndexCount > newIndexCount ) |
| | | newIndexCount = minNewIndexCount; |
| | | void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); |
| | | if ( !newIndexes ) |
| | | throw std::bad_alloc(); |
| | | indexCount = newIndexCount; |
| | | indexes = static_cast<Value **>( newIndexes ); |
| | | } |
| | | virtual void releaseArrayPageIndex( Value **indexes, |
| | | ValueInternalArray::PageIndex indexCount ) |
| | | { |
| | | if ( indexes ) |
| | | free( indexes ); |
| | | } |
| | | |
| | | virtual Value *allocateArrayPage() |
| | | { |
| | | return static_cast<Value *>( pagesAllocator_.allocate() ); |
| | | } |
| | | |
| | | virtual void releaseArrayPage( Value *value ) |
| | | { |
| | | if ( value ) |
| | | pagesAllocator_.release( value ); |
| | | } |
| | | private: |
| | | BatchAllocator<ValueInternalArray,1> arraysAllocator_; |
| | | BatchAllocator<Value,ValueInternalArray::itemsPerPage> pagesAllocator_; |
| | | }; |
| | | #endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR |
| | | |
| | | static ValueArrayAllocator *&arrayAllocator() |
| | | { |
| | | static DefaultValueArrayAllocator defaultAllocator; |
| | | static ValueArrayAllocator *arrayAllocator = &defaultAllocator; |
| | | return arrayAllocator; |
| | | } |
| | | |
| | | static struct DummyArrayAllocatorInitializer { |
| | | DummyArrayAllocatorInitializer() |
| | | { |
| | | arrayAllocator(); // ensure arrayAllocator() statics are initialized before main(). |
| | | } |
| | | } dummyArrayAllocatorInitializer; |
| | | |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // class ValueInternalArray |
| | | // ////////////////////////////////////////////////////////////////// |
| | | bool |
| | | ValueInternalArray::equals( const IteratorState &x, |
| | | const IteratorState &other ) |
| | | { |
| | | return x.array_ == other.array_ |
| | | && x.currentItemIndex_ == other.currentItemIndex_ |
| | | && x.currentPageIndex_ == other.currentPageIndex_; |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalArray::increment( IteratorState &it ) |
| | | { |
| | | JSON_ASSERT_MESSAGE( it.array_ && |
| | | (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ |
| | | != it.array_->size_, |
| | | "ValueInternalArray::increment(): moving iterator beyond end" ); |
| | | ++(it.currentItemIndex_); |
| | | if ( it.currentItemIndex_ == itemsPerPage ) |
| | | { |
| | | it.currentItemIndex_ = 0; |
| | | ++(it.currentPageIndex_); |
| | | } |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalArray::decrement( IteratorState &it ) |
| | | { |
| | | JSON_ASSERT_MESSAGE( it.array_ && it.currentPageIndex_ == it.array_->pages_ |
| | | && it.currentItemIndex_ == 0, |
| | | "ValueInternalArray::decrement(): moving iterator beyond end" ); |
| | | if ( it.currentItemIndex_ == 0 ) |
| | | { |
| | | it.currentItemIndex_ = itemsPerPage-1; |
| | | --(it.currentPageIndex_); |
| | | } |
| | | else |
| | | { |
| | | --(it.currentItemIndex_); |
| | | } |
| | | } |
| | | |
| | | |
| | | Value & |
| | | ValueInternalArray::unsafeDereference( const IteratorState &it ) |
| | | { |
| | | return (*(it.currentPageIndex_))[it.currentItemIndex_]; |
| | | } |
| | | |
| | | |
| | | Value & |
| | | ValueInternalArray::dereference( const IteratorState &it ) |
| | | { |
| | | JSON_ASSERT_MESSAGE( it.array_ && |
| | | (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ |
| | | < it.array_->size_, |
| | | "ValueInternalArray::dereference(): dereferencing invalid iterator" ); |
| | | return unsafeDereference( it ); |
| | | } |
| | | |
| | | void |
| | | ValueInternalArray::makeBeginIterator( IteratorState &it ) const |
| | | { |
| | | it.array_ = const_cast<ValueInternalArray *>( this ); |
| | | it.currentItemIndex_ = 0; |
| | | it.currentPageIndex_ = pages_; |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalArray::makeIterator( IteratorState &it, ArrayIndex index ) const |
| | | { |
| | | it.array_ = const_cast<ValueInternalArray *>( this ); |
| | | it.currentItemIndex_ = index % itemsPerPage; |
| | | it.currentPageIndex_ = pages_ + index / itemsPerPage; |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalArray::makeEndIterator( IteratorState &it ) const |
| | | { |
| | | makeIterator( it, size_ ); |
| | | } |
| | | |
| | | |
| | | ValueInternalArray::ValueInternalArray() |
| | | : pages_( 0 ) |
| | | , size_( 0 ) |
| | | , pageCount_( 0 ) |
| | | { |
| | | } |
| | | |
| | | |
| | | ValueInternalArray::ValueInternalArray( const ValueInternalArray &other ) |
| | | : pages_( 0 ) |
| | | , pageCount_( 0 ) |
| | | , size_( other.size_ ) |
| | | { |
| | | PageIndex minNewPages = other.size_ / itemsPerPage; |
| | | arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); |
| | | JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, |
| | | "ValueInternalArray::reserve(): bad reallocation" ); |
| | | IteratorState itOther; |
| | | other.makeBeginIterator( itOther ); |
| | | Value *value; |
| | | for ( ArrayIndex index = 0; index < size_; ++index, increment(itOther) ) |
| | | { |
| | | if ( index % itemsPerPage == 0 ) |
| | | { |
| | | PageIndex pageIndex = index / itemsPerPage; |
| | | value = arrayAllocator()->allocateArrayPage(); |
| | | pages_[pageIndex] = value; |
| | | } |
| | | new (value) Value( dereference( itOther ) ); |
| | | } |
| | | } |
| | | |
| | | |
| | | ValueInternalArray & |
| | | ValueInternalArray::operator =( const ValueInternalArray &other ) |
| | | { |
| | | ValueInternalArray temp( other ); |
| | | swap( temp ); |
| | | return *this; |
| | | } |
| | | |
| | | |
| | | ValueInternalArray::~ValueInternalArray() |
| | | { |
| | | // destroy all constructed items |
| | | IteratorState it; |
| | | IteratorState itEnd; |
| | | makeBeginIterator( it); |
| | | makeEndIterator( itEnd ); |
| | | for ( ; !equals(it,itEnd); increment(it) ) |
| | | { |
| | | Value *value = &dereference(it); |
| | | value->~Value(); |
| | | } |
| | | // release all pages |
| | | PageIndex lastPageIndex = size_ / itemsPerPage; |
| | | for ( PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex ) |
| | | arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); |
| | | // release pages index |
| | | arrayAllocator()->releaseArrayPageIndex( pages_, pageCount_ ); |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalArray::swap( ValueInternalArray &other ) |
| | | { |
| | | Value **tempPages = pages_; |
| | | pages_ = other.pages_; |
| | | other.pages_ = tempPages; |
| | | ArrayIndex tempSize = size_; |
| | | size_ = other.size_; |
| | | other.size_ = tempSize; |
| | | PageIndex tempPageCount = pageCount_; |
| | | pageCount_ = other.pageCount_; |
| | | other.pageCount_ = tempPageCount; |
| | | } |
| | | |
| | | void |
| | | ValueInternalArray::clear() |
| | | { |
| | | ValueInternalArray dummy; |
| | | swap( dummy ); |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalArray::resize( ArrayIndex newSize ) |
| | | { |
| | | if ( newSize == 0 ) |
| | | clear(); |
| | | else if ( newSize < size_ ) |
| | | { |
| | | IteratorState it; |
| | | IteratorState itEnd; |
| | | makeIterator( it, newSize ); |
| | | makeIterator( itEnd, size_ ); |
| | | for ( ; !equals(it,itEnd); increment(it) ) |
| | | { |
| | | Value *value = &dereference(it); |
| | | value->~Value(); |
| | | } |
| | | PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage; |
| | | PageIndex lastPageIndex = size_ / itemsPerPage; |
| | | for ( ; pageIndex < lastPageIndex; ++pageIndex ) |
| | | arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); |
| | | size_ = newSize; |
| | | } |
| | | else if ( newSize > size_ ) |
| | | resolveReference( newSize ); |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalArray::makeIndexValid( ArrayIndex index ) |
| | | { |
| | | // Need to enlarge page index ? |
| | | if ( index >= pageCount_ * itemsPerPage ) |
| | | { |
| | | PageIndex minNewPages = (index + 1) / itemsPerPage; |
| | | arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); |
| | | JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, "ValueInternalArray::reserve(): bad reallocation" ); |
| | | } |
| | | |
| | | // Need to allocate new pages ? |
| | | ArrayIndex nextPageIndex = |
| | | (size_ % itemsPerPage) != 0 ? size_ - (size_%itemsPerPage) + itemsPerPage |
| | | : size_; |
| | | if ( nextPageIndex <= index ) |
| | | { |
| | | PageIndex pageIndex = nextPageIndex / itemsPerPage; |
| | | PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1; |
| | | for ( ; pageToAllocate-- > 0; ++pageIndex ) |
| | | pages_[pageIndex] = arrayAllocator()->allocateArrayPage(); |
| | | } |
| | | |
| | | // Initialize all new entries |
| | | IteratorState it; |
| | | IteratorState itEnd; |
| | | makeIterator( it, size_ ); |
| | | size_ = index + 1; |
| | | makeIterator( itEnd, size_ ); |
| | | for ( ; !equals(it,itEnd); increment(it) ) |
| | | { |
| | | Value *value = &dereference(it); |
| | | new (value) Value(); // Construct a default value using placement new |
| | | } |
| | | } |
| | | |
| | | Value & |
| | | ValueInternalArray::resolveReference( ArrayIndex index ) |
| | | { |
| | | if ( index >= size_ ) |
| | | makeIndexValid( index ); |
| | | return pages_[index/itemsPerPage][index%itemsPerPage]; |
| | | } |
| | | |
| | | Value * |
| | | ValueInternalArray::find( ArrayIndex index ) const |
| | | { |
| | | if ( index >= size_ ) |
| | | return 0; |
| | | return &(pages_[index/itemsPerPage][index%itemsPerPage]); |
| | | } |
| | | |
| | | ValueInternalArray::ArrayIndex |
| | | ValueInternalArray::size() const |
| | | { |
| | | return size_; |
| | | } |
| | | |
| | | int |
| | | ValueInternalArray::distance( const IteratorState &x, const IteratorState &y ) |
| | | { |
| | | return indexOf(y) - indexOf(x); |
| | | } |
| | | |
| | | |
| | | ValueInternalArray::ArrayIndex |
| | | ValueInternalArray::indexOf( const IteratorState &iterator ) |
| | | { |
| | | if ( !iterator.array_ ) |
| | | return ArrayIndex(-1); |
| | | return ArrayIndex( |
| | | (iterator.currentPageIndex_ - iterator.array_->pages_) * itemsPerPage |
| | | + iterator.currentItemIndex_ ); |
| | | } |
| | | |
| | | |
| | | int |
| | | ValueInternalArray::compare( const ValueInternalArray &other ) const |
| | | { |
| | | int sizeDiff( size_ - other.size_ ); |
| | | if ( sizeDiff != 0 ) |
| | | return sizeDiff; |
| | | |
| | | for ( ArrayIndex index =0; index < size_; ++index ) |
| | | { |
| | | int diff = pages_[index/itemsPerPage][index%itemsPerPage].compare( |
| | | other.pages_[index/itemsPerPage][index%itemsPerPage] ); |
| | | if ( diff != 0 ) |
| | | return diff; |
| | | } |
| | | return 0; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | // included by json_value.cpp |
| | | // everything is within Json namespace |
| | | |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // class ValueInternalMap |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | |
| | | /** \internal MUST be safely initialized using memset( this, 0, sizeof(ValueInternalLink) ); |
| | | * This optimization is used by the fast allocator. |
| | | */ |
| | | ValueInternalLink::ValueInternalLink() |
| | | : previous_( 0 ) |
| | | , next_( 0 ) |
| | | { |
| | | } |
| | | |
| | | ValueInternalLink::~ValueInternalLink() |
| | | { |
| | | for ( int index =0; index < itemPerLink; ++index ) |
| | | { |
| | | if ( !items_[index].isItemAvailable() ) |
| | | { |
| | | if ( !items_[index].isMemberNameStatic() ) |
| | | free( keys_[index] ); |
| | | } |
| | | else |
| | | break; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | ValueMapAllocator::~ValueMapAllocator() |
| | | { |
| | | } |
| | | |
| | | #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR |
| | | class DefaultValueMapAllocator : public ValueMapAllocator |
| | | { |
| | | public: // overridden from ValueMapAllocator |
| | | virtual ValueInternalMap *newMap() |
| | | { |
| | | return new ValueInternalMap(); |
| | | } |
| | | |
| | | virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) |
| | | { |
| | | return new ValueInternalMap( other ); |
| | | } |
| | | |
| | | virtual void destructMap( ValueInternalMap *map ) |
| | | { |
| | | delete map; |
| | | } |
| | | |
| | | virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) |
| | | { |
| | | return new ValueInternalLink[size]; |
| | | } |
| | | |
| | | virtual void releaseMapBuckets( ValueInternalLink *links ) |
| | | { |
| | | delete [] links; |
| | | } |
| | | |
| | | virtual ValueInternalLink *allocateMapLink() |
| | | { |
| | | return new ValueInternalLink(); |
| | | } |
| | | |
| | | virtual void releaseMapLink( ValueInternalLink *link ) |
| | | { |
| | | delete link; |
| | | } |
| | | }; |
| | | #else |
| | | /// @todo make this thread-safe (lock when accessign batch allocator) |
| | | class DefaultValueMapAllocator : public ValueMapAllocator |
| | | { |
| | | public: // overridden from ValueMapAllocator |
| | | virtual ValueInternalMap *newMap() |
| | | { |
| | | ValueInternalMap *map = mapsAllocator_.allocate(); |
| | | new (map) ValueInternalMap(); // placement new |
| | | return map; |
| | | } |
| | | |
| | | virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) |
| | | { |
| | | ValueInternalMap *map = mapsAllocator_.allocate(); |
| | | new (map) ValueInternalMap( other ); // placement new |
| | | return map; |
| | | } |
| | | |
| | | virtual void destructMap( ValueInternalMap *map ) |
| | | { |
| | | if ( map ) |
| | | { |
| | | map->~ValueInternalMap(); |
| | | mapsAllocator_.release( map ); |
| | | } |
| | | } |
| | | |
| | | virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) |
| | | { |
| | | return new ValueInternalLink[size]; |
| | | } |
| | | |
| | | virtual void releaseMapBuckets( ValueInternalLink *links ) |
| | | { |
| | | delete [] links; |
| | | } |
| | | |
| | | virtual ValueInternalLink *allocateMapLink() |
| | | { |
| | | ValueInternalLink *link = linksAllocator_.allocate(); |
| | | memset( link, 0, sizeof(ValueInternalLink) ); |
| | | return link; |
| | | } |
| | | |
| | | virtual void releaseMapLink( ValueInternalLink *link ) |
| | | { |
| | | link->~ValueInternalLink(); |
| | | linksAllocator_.release( link ); |
| | | } |
| | | private: |
| | | BatchAllocator<ValueInternalMap,1> mapsAllocator_; |
| | | BatchAllocator<ValueInternalLink,1> linksAllocator_; |
| | | }; |
| | | #endif |
| | | |
| | | static ValueMapAllocator *&mapAllocator() |
| | | { |
| | | static DefaultValueMapAllocator defaultAllocator; |
| | | static ValueMapAllocator *mapAllocator = &defaultAllocator; |
| | | return mapAllocator; |
| | | } |
| | | |
| | | static struct DummyMapAllocatorInitializer { |
| | | DummyMapAllocatorInitializer() |
| | | { |
| | | mapAllocator(); // ensure mapAllocator() statics are initialized before main(). |
| | | } |
| | | } dummyMapAllocatorInitializer; |
| | | |
| | | |
| | | |
| | | // h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32. |
| | | |
| | | /* |
| | | use linked list hash map. |
| | | buckets array is a container. |
| | | linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124) |
| | | value have extra state: valid, available, deleted |
| | | */ |
| | | |
| | | |
| | | ValueInternalMap::ValueInternalMap() |
| | | : buckets_( 0 ) |
| | | , tailLink_( 0 ) |
| | | , bucketsSize_( 0 ) |
| | | , itemCount_( 0 ) |
| | | { |
| | | } |
| | | |
| | | |
| | | ValueInternalMap::ValueInternalMap( const ValueInternalMap &other ) |
| | | : buckets_( 0 ) |
| | | , tailLink_( 0 ) |
| | | , bucketsSize_( 0 ) |
| | | , itemCount_( 0 ) |
| | | { |
| | | reserve( other.itemCount_ ); |
| | | IteratorState it; |
| | | IteratorState itEnd; |
| | | other.makeBeginIterator( it ); |
| | | other.makeEndIterator( itEnd ); |
| | | for ( ; !equals(it,itEnd); increment(it) ) |
| | | { |
| | | bool isStatic; |
| | | const char *memberName = key( it, isStatic ); |
| | | const Value &aValue = value( it ); |
| | | resolveReference(memberName, isStatic) = aValue; |
| | | } |
| | | } |
| | | |
| | | |
| | | ValueInternalMap & |
| | | ValueInternalMap::operator =( const ValueInternalMap &other ) |
| | | { |
| | | ValueInternalMap dummy( other ); |
| | | swap( dummy ); |
| | | return *this; |
| | | } |
| | | |
| | | |
| | | ValueInternalMap::~ValueInternalMap() |
| | | { |
| | | if ( buckets_ ) |
| | | { |
| | | for ( BucketIndex bucketIndex =0; bucketIndex < bucketsSize_; ++bucketIndex ) |
| | | { |
| | | ValueInternalLink *link = buckets_[bucketIndex].next_; |
| | | while ( link ) |
| | | { |
| | | ValueInternalLink *linkToRelease = link; |
| | | link = link->next_; |
| | | mapAllocator()->releaseMapLink( linkToRelease ); |
| | | } |
| | | } |
| | | mapAllocator()->releaseMapBuckets( buckets_ ); |
| | | } |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalMap::swap( ValueInternalMap &other ) |
| | | { |
| | | ValueInternalLink *tempBuckets = buckets_; |
| | | buckets_ = other.buckets_; |
| | | other.buckets_ = tempBuckets; |
| | | ValueInternalLink *tempTailLink = tailLink_; |
| | | tailLink_ = other.tailLink_; |
| | | other.tailLink_ = tempTailLink; |
| | | BucketIndex tempBucketsSize = bucketsSize_; |
| | | bucketsSize_ = other.bucketsSize_; |
| | | other.bucketsSize_ = tempBucketsSize; |
| | | BucketIndex tempItemCount = itemCount_; |
| | | itemCount_ = other.itemCount_; |
| | | other.itemCount_ = tempItemCount; |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalMap::clear() |
| | | { |
| | | ValueInternalMap dummy; |
| | | swap( dummy ); |
| | | } |
| | | |
| | | |
| | | ValueInternalMap::BucketIndex |
| | | ValueInternalMap::size() const |
| | | { |
| | | return itemCount_; |
| | | } |
| | | |
| | | bool |
| | | ValueInternalMap::reserveDelta( BucketIndex growth ) |
| | | { |
| | | return reserve( itemCount_ + growth ); |
| | | } |
| | | |
| | | bool |
| | | ValueInternalMap::reserve( BucketIndex newItemCount ) |
| | | { |
| | | if ( !buckets_ && newItemCount > 0 ) |
| | | { |
| | | buckets_ = mapAllocator()->allocateMapBuckets( 1 ); |
| | | bucketsSize_ = 1; |
| | | tailLink_ = &buckets_[0]; |
| | | } |
| | | // BucketIndex idealBucketCount = (newItemCount + ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink; |
| | | return true; |
| | | } |
| | | |
| | | |
| | | const Value * |
| | | ValueInternalMap::find( const char *key ) const |
| | | { |
| | | if ( !bucketsSize_ ) |
| | | return 0; |
| | | HashKey hashedKey = hash( key ); |
| | | BucketIndex bucketIndex = hashedKey % bucketsSize_; |
| | | for ( const ValueInternalLink *current = &buckets_[bucketIndex]; |
| | | current != 0; |
| | | current = current->next_ ) |
| | | { |
| | | for ( BucketIndex index=0; index < ValueInternalLink::itemPerLink; ++index ) |
| | | { |
| | | if ( current->items_[index].isItemAvailable() ) |
| | | return 0; |
| | | if ( strcmp( key, current->keys_[index] ) == 0 ) |
| | | return ¤t->items_[index]; |
| | | } |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | |
| | | Value * |
| | | ValueInternalMap::find( const char *key ) |
| | | { |
| | | const ValueInternalMap *constThis = this; |
| | | return const_cast<Value *>( constThis->find( key ) ); |
| | | } |
| | | |
| | | |
| | | Value & |
| | | ValueInternalMap::resolveReference( const char *key, |
| | | bool isStatic ) |
| | | { |
| | | HashKey hashedKey = hash( key ); |
| | | if ( bucketsSize_ ) |
| | | { |
| | | BucketIndex bucketIndex = hashedKey % bucketsSize_; |
| | | ValueInternalLink **previous = 0; |
| | | BucketIndex index; |
| | | for ( ValueInternalLink *current = &buckets_[bucketIndex]; |
| | | current != 0; |
| | | previous = ¤t->next_, current = current->next_ ) |
| | | { |
| | | for ( index=0; index < ValueInternalLink::itemPerLink; ++index ) |
| | | { |
| | | if ( current->items_[index].isItemAvailable() ) |
| | | return setNewItem( key, isStatic, current, index ); |
| | | if ( strcmp( key, current->keys_[index] ) == 0 ) |
| | | return current->items_[index]; |
| | | } |
| | | } |
| | | } |
| | | |
| | | reserveDelta( 1 ); |
| | | return unsafeAdd( key, isStatic, hashedKey ); |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalMap::remove( const char *key ) |
| | | { |
| | | HashKey hashedKey = hash( key ); |
| | | if ( !bucketsSize_ ) |
| | | return; |
| | | BucketIndex bucketIndex = hashedKey % bucketsSize_; |
| | | for ( ValueInternalLink *link = &buckets_[bucketIndex]; |
| | | link != 0; |
| | | link = link->next_ ) |
| | | { |
| | | BucketIndex index; |
| | | for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) |
| | | { |
| | | if ( link->items_[index].isItemAvailable() ) |
| | | return; |
| | | if ( strcmp( key, link->keys_[index] ) == 0 ) |
| | | { |
| | | doActualRemove( link, index, bucketIndex ); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | void |
| | | ValueInternalMap::doActualRemove( ValueInternalLink *link, |
| | | BucketIndex index, |
| | | BucketIndex bucketIndex ) |
| | | { |
| | | // find last item of the bucket and swap it with the 'removed' one. |
| | | // set removed items flags to 'available'. |
| | | // if last page only contains 'available' items, then desallocate it (it's empty) |
| | | ValueInternalLink *&lastLink = getLastLinkInBucket( index ); |
| | | BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1 |
| | | for ( ; |
| | | lastItemIndex < ValueInternalLink::itemPerLink; |
| | | ++lastItemIndex ) // may be optimized with dicotomic search |
| | | { |
| | | if ( lastLink->items_[lastItemIndex].isItemAvailable() ) |
| | | break; |
| | | } |
| | | |
| | | BucketIndex lastUsedIndex = lastItemIndex - 1; |
| | | Value *valueToDelete = &link->items_[index]; |
| | | Value *valueToPreserve = &lastLink->items_[lastUsedIndex]; |
| | | if ( valueToDelete != valueToPreserve ) |
| | | valueToDelete->swap( *valueToPreserve ); |
| | | if ( lastUsedIndex == 0 ) // page is now empty |
| | | { // remove it from bucket linked list and delete it. |
| | | ValueInternalLink *linkPreviousToLast = lastLink->previous_; |
| | | if ( linkPreviousToLast != 0 ) // can not deleted bucket link. |
| | | { |
| | | mapAllocator()->releaseMapLink( lastLink ); |
| | | linkPreviousToLast->next_ = 0; |
| | | lastLink = linkPreviousToLast; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | Value dummy; |
| | | valueToPreserve->swap( dummy ); // restore deleted to default Value. |
| | | valueToPreserve->setItemUsed( false ); |
| | | } |
| | | --itemCount_; |
| | | } |
| | | |
| | | |
| | | ValueInternalLink *& |
| | | ValueInternalMap::getLastLinkInBucket( BucketIndex bucketIndex ) |
| | | { |
| | | if ( bucketIndex == bucketsSize_ - 1 ) |
| | | return tailLink_; |
| | | ValueInternalLink *&previous = buckets_[bucketIndex+1].previous_; |
| | | if ( !previous ) |
| | | previous = &buckets_[bucketIndex]; |
| | | return previous; |
| | | } |
| | | |
| | | |
| | | Value & |
| | | ValueInternalMap::setNewItem( const char *key, |
| | | bool isStatic, |
| | | ValueInternalLink *link, |
| | | BucketIndex index ) |
| | | { |
| | | char *duplicatedKey = valueAllocator()->makeMemberName( key ); |
| | | ++itemCount_; |
| | | link->keys_[index] = duplicatedKey; |
| | | link->items_[index].setItemUsed(); |
| | | link->items_[index].setMemberNameIsStatic( isStatic ); |
| | | return link->items_[index]; // items already default constructed. |
| | | } |
| | | |
| | | |
| | | Value & |
| | | ValueInternalMap::unsafeAdd( const char *key, |
| | | bool isStatic, |
| | | HashKey hashedKey ) |
| | | { |
| | | JSON_ASSERT_MESSAGE( bucketsSize_ > 0, "ValueInternalMap::unsafeAdd(): internal logic error." ); |
| | | BucketIndex bucketIndex = hashedKey % bucketsSize_; |
| | | ValueInternalLink *&previousLink = getLastLinkInBucket( bucketIndex ); |
| | | ValueInternalLink *link = previousLink; |
| | | BucketIndex index; |
| | | for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) |
| | | { |
| | | if ( link->items_[index].isItemAvailable() ) |
| | | break; |
| | | } |
| | | if ( index == ValueInternalLink::itemPerLink ) // need to add a new page |
| | | { |
| | | ValueInternalLink *newLink = mapAllocator()->allocateMapLink(); |
| | | index = 0; |
| | | link->next_ = newLink; |
| | | previousLink = newLink; |
| | | link = newLink; |
| | | } |
| | | return setNewItem( key, isStatic, link, index ); |
| | | } |
| | | |
| | | |
| | | ValueInternalMap::HashKey |
| | | ValueInternalMap::hash( const char *key ) const |
| | | { |
| | | HashKey hash = 0; |
| | | while ( *key ) |
| | | hash += *key++ * 37; |
| | | return hash; |
| | | } |
| | | |
| | | |
| | | int |
| | | ValueInternalMap::compare( const ValueInternalMap &other ) const |
| | | { |
| | | int sizeDiff( itemCount_ - other.itemCount_ ); |
| | | if ( sizeDiff != 0 ) |
| | | return sizeDiff; |
| | | // Strict order guaranty is required. Compare all keys FIRST, then compare values. |
| | | IteratorState it; |
| | | IteratorState itEnd; |
| | | makeBeginIterator( it ); |
| | | makeEndIterator( itEnd ); |
| | | for ( ; !equals(it,itEnd); increment(it) ) |
| | | { |
| | | if ( !other.find( key( it ) ) ) |
| | | return 1; |
| | | } |
| | | |
| | | // All keys are equals, let's compare values |
| | | makeBeginIterator( it ); |
| | | for ( ; !equals(it,itEnd); increment(it) ) |
| | | { |
| | | const Value *otherValue = other.find( key( it ) ); |
| | | int valueDiff = value(it).compare( *otherValue ); |
| | | if ( valueDiff != 0 ) |
| | | return valueDiff; |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalMap::makeBeginIterator( IteratorState &it ) const |
| | | { |
| | | it.map_ = const_cast<ValueInternalMap *>( this ); |
| | | it.bucketIndex_ = 0; |
| | | it.itemIndex_ = 0; |
| | | it.link_ = buckets_; |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalMap::makeEndIterator( IteratorState &it ) const |
| | | { |
| | | it.map_ = const_cast<ValueInternalMap *>( this ); |
| | | it.bucketIndex_ = bucketsSize_; |
| | | it.itemIndex_ = 0; |
| | | it.link_ = 0; |
| | | } |
| | | |
| | | |
| | | bool |
| | | ValueInternalMap::equals( const IteratorState &x, const IteratorState &other ) |
| | | { |
| | | return x.map_ == other.map_ |
| | | && x.bucketIndex_ == other.bucketIndex_ |
| | | && x.link_ == other.link_ |
| | | && x.itemIndex_ == other.itemIndex_; |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalMap::incrementBucket( IteratorState &iterator ) |
| | | { |
| | | ++iterator.bucketIndex_; |
| | | JSON_ASSERT_MESSAGE( iterator.bucketIndex_ <= iterator.map_->bucketsSize_, |
| | | "ValueInternalMap::increment(): attempting to iterate beyond end." ); |
| | | if ( iterator.bucketIndex_ == iterator.map_->bucketsSize_ ) |
| | | iterator.link_ = 0; |
| | | else |
| | | iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]); |
| | | iterator.itemIndex_ = 0; |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalMap::increment( IteratorState &iterator ) |
| | | { |
| | | JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterator using invalid iterator." ); |
| | | ++iterator.itemIndex_; |
| | | if ( iterator.itemIndex_ == ValueInternalLink::itemPerLink ) |
| | | { |
| | | JSON_ASSERT_MESSAGE( iterator.link_ != 0, |
| | | "ValueInternalMap::increment(): attempting to iterate beyond end." ); |
| | | iterator.link_ = iterator.link_->next_; |
| | | if ( iterator.link_ == 0 ) |
| | | incrementBucket( iterator ); |
| | | } |
| | | else if ( iterator.link_->items_[iterator.itemIndex_].isItemAvailable() ) |
| | | { |
| | | incrementBucket( iterator ); |
| | | } |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueInternalMap::decrement( IteratorState &iterator ) |
| | | { |
| | | if ( iterator.itemIndex_ == 0 ) |
| | | { |
| | | JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterate using invalid iterator." ); |
| | | if ( iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_] ) |
| | | { |
| | | JSON_ASSERT_MESSAGE( iterator.bucketIndex_ > 0, "Attempting to iterate beyond beginning." ); |
| | | --(iterator.bucketIndex_); |
| | | } |
| | | iterator.link_ = iterator.link_->previous_; |
| | | iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1; |
| | | } |
| | | } |
| | | |
| | | |
| | | const char * |
| | | ValueInternalMap::key( const IteratorState &iterator ) |
| | | { |
| | | JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); |
| | | return iterator.link_->keys_[iterator.itemIndex_]; |
| | | } |
| | | |
| | | const char * |
| | | ValueInternalMap::key( const IteratorState &iterator, bool &isStatic ) |
| | | { |
| | | JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); |
| | | isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic(); |
| | | return iterator.link_->keys_[iterator.itemIndex_]; |
| | | } |
| | | |
| | | |
| | | Value & |
| | | ValueInternalMap::value( const IteratorState &iterator ) |
| | | { |
| | | JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); |
| | | return iterator.link_->items_[iterator.itemIndex_]; |
| | | } |
| | | |
| | | |
| | | int |
| | | ValueInternalMap::distance( const IteratorState &x, const IteratorState &y ) |
| | | { |
| | | int offset = 0; |
| | | IteratorState it = x; |
| | | while ( !equals( it, y ) ) |
| | | increment( it ); |
| | | return offset; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #include <json/reader.h> |
| | | #include <json/value.h> |
| | | #include <utility> |
| | | #include <cstdio> |
| | | #include <cassert> |
| | | #include <cstring> |
| | | #include <iostream> |
| | | #include <stdexcept> |
| | | |
| | | #if _MSC_VER >= 1400 // VC++ 8.0 |
| | | #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. |
| | | #endif |
| | | |
| | | namespace Json { |
| | | |
| | | // Implementation of class Features |
| | | // //////////////////////////////// |
| | | |
| | | Features::Features() |
| | | : allowComments_( true ) |
| | | , strictRoot_( false ) |
| | | { |
| | | } |
| | | |
| | | |
| | | Features |
| | | Features::all() |
| | | { |
| | | return Features(); |
| | | } |
| | | |
| | | |
| | | Features |
| | | Features::strictMode() |
| | | { |
| | | Features features; |
| | | features.allowComments_ = false; |
| | | features.strictRoot_ = true; |
| | | return features; |
| | | } |
| | | |
| | | // Implementation of class Reader |
| | | // //////////////////////////////// |
| | | |
| | | |
| | | static inline bool |
| | | in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) |
| | | { |
| | | return c == c1 || c == c2 || c == c3 || c == c4; |
| | | } |
| | | |
| | | static inline bool |
| | | in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) |
| | | { |
| | | return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; |
| | | } |
| | | |
| | | |
| | | static bool |
| | | containsNewLine( Reader::Location begin, |
| | | Reader::Location end ) |
| | | { |
| | | for ( ;begin < end; ++begin ) |
| | | if ( *begin == '\n' || *begin == '\r' ) |
| | | return true; |
| | | return false; |
| | | } |
| | | |
| | | static std::string codePointToUTF8(unsigned int cp) |
| | | { |
| | | std::string result; |
| | | |
| | | // based on description from http://en.wikipedia.org/wiki/UTF-8 |
| | | |
| | | if (cp <= 0x7f) |
| | | { |
| | | result.resize(1); |
| | | result[0] = static_cast<char>(cp); |
| | | } |
| | | else if (cp <= 0x7FF) |
| | | { |
| | | result.resize(2); |
| | | result[1] = static_cast<char>(0x80 | (0x3f & cp)); |
| | | result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6))); |
| | | } |
| | | else if (cp <= 0xFFFF) |
| | | { |
| | | result.resize(3); |
| | | result[2] = static_cast<char>(0x80 | (0x3f & cp)); |
| | | result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6))); |
| | | result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12))); |
| | | } |
| | | else if (cp <= 0x10FFFF) |
| | | { |
| | | result.resize(4); |
| | | result[3] = static_cast<char>(0x80 | (0x3f & cp)); |
| | | result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6))); |
| | | result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12))); |
| | | result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18))); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | |
| | | // Class Reader |
| | | // ////////////////////////////////////////////////////////////////// |
| | | |
| | | Reader::Reader() |
| | | : features_( Features::all() ) |
| | | { |
| | | } |
| | | |
| | | |
| | | Reader::Reader( const Features &features ) |
| | | : features_( features ) |
| | | { |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::parse( const std::string &document, |
| | | Value &root, |
| | | bool collectComments ) |
| | | { |
| | | document_ = document; |
| | | const char *begin = document_.c_str(); |
| | | const char *end = begin + document_.length(); |
| | | return parse( begin, end, root, collectComments ); |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::parse( std::istream& sin, |
| | | Value &root, |
| | | bool collectComments ) |
| | | { |
| | | //std::istream_iterator<char> begin(sin); |
| | | //std::istream_iterator<char> end; |
| | | // Those would allow streamed input from a file, if parse() were a |
| | | // template function. |
| | | |
| | | // Since std::string is reference-counted, this at least does not |
| | | // create an extra copy. |
| | | std::string doc; |
| | | std::getline(sin, doc, (char)EOF); |
| | | return parse( doc, root, collectComments ); |
| | | } |
| | | |
| | | bool |
| | | Reader::parse( const char *beginDoc, const char *endDoc, |
| | | Value &root, |
| | | bool collectComments ) |
| | | { |
| | | if ( !features_.allowComments_ ) |
| | | { |
| | | collectComments = false; |
| | | } |
| | | |
| | | begin_ = beginDoc; |
| | | end_ = endDoc; |
| | | collectComments_ = collectComments; |
| | | current_ = begin_; |
| | | lastValueEnd_ = 0; |
| | | lastValue_ = 0; |
| | | commentsBefore_ = ""; |
| | | errors_.clear(); |
| | | while ( !nodes_.empty() ) |
| | | nodes_.pop(); |
| | | nodes_.push( &root ); |
| | | |
| | | bool successful = readValue(); |
| | | Token token; |
| | | skipCommentTokens( token ); |
| | | if ( collectComments_ && !commentsBefore_.empty() ) |
| | | root.setComment( commentsBefore_, commentAfter ); |
| | | if ( features_.strictRoot_ ) |
| | | { |
| | | if ( !root.isArray() && !root.isObject() ) |
| | | { |
| | | // Set error location to start of doc, ideally should be first token found in doc |
| | | token.type_ = tokenError; |
| | | token.start_ = beginDoc; |
| | | token.end_ = endDoc; |
| | | addError( "A valid JSON document must be either an array or an object value.", |
| | | token ); |
| | | return false; |
| | | } |
| | | } |
| | | return successful; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::readValue() |
| | | { |
| | | Token token; |
| | | skipCommentTokens( token ); |
| | | bool successful = true; |
| | | |
| | | if ( collectComments_ && !commentsBefore_.empty() ) |
| | | { |
| | | currentValue().setComment( commentsBefore_, commentBefore ); |
| | | commentsBefore_ = ""; |
| | | } |
| | | |
| | | |
| | | switch ( token.type_ ) |
| | | { |
| | | case tokenObjectBegin: |
| | | successful = readObject( token ); |
| | | break; |
| | | case tokenArrayBegin: |
| | | successful = readArray( token ); |
| | | break; |
| | | case tokenNumber: |
| | | successful = decodeNumber( token ); |
| | | break; |
| | | case tokenString: |
| | | successful = decodeString( token ); |
| | | break; |
| | | case tokenTrue: |
| | | currentValue() = true; |
| | | break; |
| | | case tokenFalse: |
| | | currentValue() = false; |
| | | break; |
| | | case tokenNull: |
| | | currentValue() = Value(); |
| | | break; |
| | | default: |
| | | return addError( "Syntax error: value, object or array expected.", token ); |
| | | } |
| | | |
| | | if ( collectComments_ ) |
| | | { |
| | | lastValueEnd_ = current_; |
| | | lastValue_ = ¤tValue(); |
| | | } |
| | | |
| | | return successful; |
| | | } |
| | | |
| | | |
| | | void |
| | | Reader::skipCommentTokens( Token &token ) |
| | | { |
| | | if ( features_.allowComments_ ) |
| | | { |
| | | do |
| | | { |
| | | readToken( token ); |
| | | } |
| | | while ( token.type_ == tokenComment ); |
| | | } |
| | | else |
| | | { |
| | | readToken( token ); |
| | | } |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::expectToken( TokenType type, Token &token, const char *message ) |
| | | { |
| | | readToken( token ); |
| | | if ( token.type_ != type ) |
| | | return addError( message, token ); |
| | | return true; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::readToken( Token &token ) |
| | | { |
| | | skipSpaces(); |
| | | token.start_ = current_; |
| | | Char c = getNextChar(); |
| | | bool ok = true; |
| | | switch ( c ) |
| | | { |
| | | case '{': |
| | | token.type_ = tokenObjectBegin; |
| | | break; |
| | | case '}': |
| | | token.type_ = tokenObjectEnd; |
| | | break; |
| | | case '[': |
| | | token.type_ = tokenArrayBegin; |
| | | break; |
| | | case ']': |
| | | token.type_ = tokenArrayEnd; |
| | | break; |
| | | case '"': |
| | | token.type_ = tokenString; |
| | | ok = readString(); |
| | | break; |
| | | case '/': |
| | | token.type_ = tokenComment; |
| | | ok = readComment(); |
| | | break; |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | case '6': |
| | | case '7': |
| | | case '8': |
| | | case '9': |
| | | case '-': |
| | | token.type_ = tokenNumber; |
| | | readNumber(); |
| | | break; |
| | | case 't': |
| | | token.type_ = tokenTrue; |
| | | ok = match( "rue", 3 ); |
| | | break; |
| | | case 'f': |
| | | token.type_ = tokenFalse; |
| | | ok = match( "alse", 4 ); |
| | | break; |
| | | case 'n': |
| | | token.type_ = tokenNull; |
| | | ok = match( "ull", 3 ); |
| | | break; |
| | | case ',': |
| | | token.type_ = tokenArraySeparator; |
| | | break; |
| | | case ':': |
| | | token.type_ = tokenMemberSeparator; |
| | | break; |
| | | case 0: |
| | | token.type_ = tokenEndOfStream; |
| | | break; |
| | | default: |
| | | ok = false; |
| | | break; |
| | | } |
| | | if ( !ok ) |
| | | token.type_ = tokenError; |
| | | token.end_ = current_; |
| | | return true; |
| | | } |
| | | |
| | | |
| | | void |
| | | Reader::skipSpaces() |
| | | { |
| | | while ( current_ != end_ ) |
| | | { |
| | | Char c = *current_; |
| | | if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) |
| | | ++current_; |
| | | else |
| | | break; |
| | | } |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::match( Location pattern, |
| | | int patternLength ) |
| | | { |
| | | if ( end_ - current_ < patternLength ) |
| | | return false; |
| | | int index = patternLength; |
| | | while ( index-- ) |
| | | if ( current_[index] != pattern[index] ) |
| | | return false; |
| | | current_ += patternLength; |
| | | return true; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::readComment() |
| | | { |
| | | Location commentBegin = current_ - 1; |
| | | Char c = getNextChar(); |
| | | bool successful = false; |
| | | if ( c == '*' ) |
| | | successful = readCStyleComment(); |
| | | else if ( c == '/' ) |
| | | successful = readCppStyleComment(); |
| | | if ( !successful ) |
| | | return false; |
| | | |
| | | if ( collectComments_ ) |
| | | { |
| | | CommentPlacement placement = commentBefore; |
| | | if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) |
| | | { |
| | | if ( c != '*' || !containsNewLine( commentBegin, current_ ) ) |
| | | placement = commentAfterOnSameLine; |
| | | } |
| | | |
| | | addComment( commentBegin, current_, placement ); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | |
| | | void |
| | | Reader::addComment( Location begin, |
| | | Location end, |
| | | CommentPlacement placement ) |
| | | { |
| | | assert( collectComments_ ); |
| | | if ( placement == commentAfterOnSameLine ) |
| | | { |
| | | assert( lastValue_ != 0 ); |
| | | lastValue_->setComment( std::string( begin, end ), placement ); |
| | | } |
| | | else |
| | | { |
| | | if ( !commentsBefore_.empty() ) |
| | | commentsBefore_ += "\n"; |
| | | commentsBefore_ += std::string( begin, end ); |
| | | } |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::readCStyleComment() |
| | | { |
| | | while ( current_ != end_ ) |
| | | { |
| | | Char c = getNextChar(); |
| | | if ( c == '*' && *current_ == '/' ) |
| | | break; |
| | | } |
| | | return getNextChar() == '/'; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::readCppStyleComment() |
| | | { |
| | | while ( current_ != end_ ) |
| | | { |
| | | Char c = getNextChar(); |
| | | if ( c == '\r' || c == '\n' ) |
| | | break; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | |
| | | void |
| | | Reader::readNumber() |
| | | { |
| | | while ( current_ != end_ ) |
| | | { |
| | | if ( !(*current_ >= '0' && *current_ <= '9') && |
| | | !in( *current_, '.', 'e', 'E', '+', '-' ) ) |
| | | break; |
| | | ++current_; |
| | | } |
| | | } |
| | | |
| | | bool |
| | | Reader::readString() |
| | | { |
| | | Char c = 0; |
| | | while ( current_ != end_ ) |
| | | { |
| | | c = getNextChar(); |
| | | if ( c == '\\' ) |
| | | getNextChar(); |
| | | else if ( c == '"' ) |
| | | break; |
| | | } |
| | | return c == '"'; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::readObject( Token &tokenStart ) |
| | | { |
| | | Token tokenName; |
| | | std::string name; |
| | | currentValue() = Value( objectValue ); |
| | | while ( readToken( tokenName ) ) |
| | | { |
| | | bool initialTokenOk = true; |
| | | while ( tokenName.type_ == tokenComment && initialTokenOk ) |
| | | initialTokenOk = readToken( tokenName ); |
| | | if ( !initialTokenOk ) |
| | | break; |
| | | if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object |
| | | return true; |
| | | if ( tokenName.type_ != tokenString ) |
| | | break; |
| | | |
| | | name = ""; |
| | | if ( !decodeString( tokenName, name ) ) |
| | | return recoverFromError( tokenObjectEnd ); |
| | | |
| | | Token colon; |
| | | if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) |
| | | { |
| | | return addErrorAndRecover( "Missing ':' after object member name", |
| | | colon, |
| | | tokenObjectEnd ); |
| | | } |
| | | Value &value = currentValue()[ name ]; |
| | | nodes_.push( &value ); |
| | | bool ok = readValue(); |
| | | nodes_.pop(); |
| | | if ( !ok ) // error already set |
| | | return recoverFromError( tokenObjectEnd ); |
| | | |
| | | Token comma; |
| | | if ( !readToken( comma ) |
| | | || ( comma.type_ != tokenObjectEnd && |
| | | comma.type_ != tokenArraySeparator && |
| | | comma.type_ != tokenComment ) ) |
| | | { |
| | | return addErrorAndRecover( "Missing ',' or '}' in object declaration", |
| | | comma, |
| | | tokenObjectEnd ); |
| | | } |
| | | bool finalizeTokenOk = true; |
| | | while ( comma.type_ == tokenComment && |
| | | finalizeTokenOk ) |
| | | finalizeTokenOk = readToken( comma ); |
| | | if ( comma.type_ == tokenObjectEnd ) |
| | | return true; |
| | | } |
| | | return addErrorAndRecover( "Missing '}' or object member name", |
| | | tokenName, |
| | | tokenObjectEnd ); |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::readArray( Token &tokenStart ) |
| | | { |
| | | currentValue() = Value( arrayValue ); |
| | | skipSpaces(); |
| | | if ( *current_ == ']' ) // empty array |
| | | { |
| | | Token endArray; |
| | | readToken( endArray ); |
| | | return true; |
| | | } |
| | | int index = 0; |
| | | while ( true ) |
| | | { |
| | | Value &value = currentValue()[ index++ ]; |
| | | nodes_.push( &value ); |
| | | bool ok = readValue(); |
| | | nodes_.pop(); |
| | | if ( !ok ) // error already set |
| | | return recoverFromError( tokenArrayEnd ); |
| | | |
| | | Token token; |
| | | // Accept Comment after last item in the array. |
| | | ok = readToken( token ); |
| | | while ( token.type_ == tokenComment && ok ) |
| | | { |
| | | ok = readToken( token ); |
| | | } |
| | | bool badTokenType = ( token.type_ == tokenArraySeparator && |
| | | token.type_ == tokenArrayEnd ); |
| | | if ( !ok || badTokenType ) |
| | | { |
| | | return addErrorAndRecover( "Missing ',' or ']' in array declaration", |
| | | token, |
| | | tokenArrayEnd ); |
| | | } |
| | | if ( token.type_ == tokenArrayEnd ) |
| | | break; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::decodeNumber( Token &token ) |
| | | { |
| | | bool isDouble = false; |
| | | for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) |
| | | { |
| | | isDouble = isDouble |
| | | || in( *inspect, '.', 'e', 'E', '+' ) |
| | | || ( *inspect == '-' && inspect != token.start_ ); |
| | | } |
| | | if ( isDouble ) |
| | | return decodeDouble( token ); |
| | | Location current = token.start_; |
| | | bool isNegative = *current == '-'; |
| | | if ( isNegative ) |
| | | ++current; |
| | | Value::UInt threshold = (isNegative ? Value::UInt(-Value::minInt) |
| | | : Value::maxUInt) / 10; |
| | | Value::UInt value = 0; |
| | | while ( current < token.end_ ) |
| | | { |
| | | Char c = *current++; |
| | | if ( c < '0' || c > '9' ) |
| | | return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); |
| | | if ( value >= threshold ) |
| | | return decodeDouble( token ); |
| | | value = value * 10 + Value::UInt(c - '0'); |
| | | } |
| | | if ( isNegative ) |
| | | currentValue() = -Value::Int( value ); |
| | | else if ( value <= Value::UInt(Value::maxInt) ) |
| | | currentValue() = Value::Int( value ); |
| | | else |
| | | currentValue() = value; |
| | | return true; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::decodeDouble( Token &token ) |
| | | { |
| | | double value = 0; |
| | | const int bufferSize = 32; |
| | | int count; |
| | | int length = int(token.end_ - token.start_); |
| | | if ( length <= bufferSize ) |
| | | { |
| | | Char buffer[bufferSize]; |
| | | memcpy( buffer, token.start_, length ); |
| | | buffer[length] = 0; |
| | | count = sscanf( buffer, "%lf", &value ); |
| | | } |
| | | else |
| | | { |
| | | std::string buffer( token.start_, token.end_ ); |
| | | count = sscanf( buffer.c_str(), "%lf", &value ); |
| | | } |
| | | |
| | | if ( count != 1 ) |
| | | return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); |
| | | currentValue() = value; |
| | | return true; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::decodeString( Token &token ) |
| | | { |
| | | std::string decoded; |
| | | if ( !decodeString( token, decoded ) ) |
| | | return false; |
| | | currentValue() = decoded; |
| | | return true; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::decodeString( Token &token, std::string &decoded ) |
| | | { |
| | | decoded.reserve( token.end_ - token.start_ - 2 ); |
| | | Location current = token.start_ + 1; // skip '"' |
| | | Location end = token.end_ - 1; // do not include '"' |
| | | while ( current != end ) |
| | | { |
| | | Char c = *current++; |
| | | if ( c == '"' ) |
| | | break; |
| | | else if ( c == '\\' ) |
| | | { |
| | | if ( current == end ) |
| | | return addError( "Empty escape sequence in string", token, current ); |
| | | Char escape = *current++; |
| | | switch ( escape ) |
| | | { |
| | | case '"': decoded += '"'; break; |
| | | case '/': decoded += '/'; break; |
| | | case '\\': decoded += '\\'; break; |
| | | case 'b': decoded += '\b'; break; |
| | | case 'f': decoded += '\f'; break; |
| | | case 'n': decoded += '\n'; break; |
| | | case 'r': decoded += '\r'; break; |
| | | case 't': decoded += '\t'; break; |
| | | case 'u': |
| | | { |
| | | unsigned int unicode; |
| | | if ( !decodeUnicodeCodePoint( token, current, end, unicode ) ) |
| | | return false; |
| | | decoded += codePointToUTF8(unicode); |
| | | } |
| | | break; |
| | | default: |
| | | return addError( "Bad escape sequence in string", token, current ); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | decoded += c; |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | bool |
| | | Reader::decodeUnicodeCodePoint( Token &token, |
| | | Location ¤t, |
| | | Location end, |
| | | unsigned int &unicode ) |
| | | { |
| | | |
| | | if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) ) |
| | | return false; |
| | | if (unicode >= 0xD800 && unicode <= 0xDBFF) |
| | | { |
| | | // surrogate pairs |
| | | if (end - current < 6) |
| | | return addError( "additional six characters expected to parse unicode surrogate pair.", token, current ); |
| | | unsigned int surrogatePair; |
| | | if (*(current++) == '\\' && *(current++)== 'u') |
| | | { |
| | | if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair )) |
| | | { |
| | | unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); |
| | | } |
| | | else |
| | | return false; |
| | | } |
| | | else |
| | | return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | bool |
| | | Reader::decodeUnicodeEscapeSequence( Token &token, |
| | | Location ¤t, |
| | | Location end, |
| | | unsigned int &unicode ) |
| | | { |
| | | if ( end - current < 4 ) |
| | | return addError( "Bad unicode escape sequence in string: four digits expected.", token, current ); |
| | | unicode = 0; |
| | | for ( int index =0; index < 4; ++index ) |
| | | { |
| | | Char c = *current++; |
| | | unicode *= 16; |
| | | if ( c >= '0' && c <= '9' ) |
| | | unicode += c - '0'; |
| | | else if ( c >= 'a' && c <= 'f' ) |
| | | unicode += c - 'a' + 10; |
| | | else if ( c >= 'A' && c <= 'F' ) |
| | | unicode += c - 'A' + 10; |
| | | else |
| | | return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current ); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::addError( const std::string &message, |
| | | Token &token, |
| | | Location extra ) |
| | | { |
| | | ErrorInfo info; |
| | | info.token_ = token; |
| | | info.message_ = message; |
| | | info.extra_ = extra; |
| | | errors_.push_back( info ); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::recoverFromError( TokenType skipUntilToken ) |
| | | { |
| | | int errorCount = int(errors_.size()); |
| | | Token skip; |
| | | while ( true ) |
| | | { |
| | | if ( !readToken(skip) ) |
| | | errors_.resize( errorCount ); // discard errors caused by recovery |
| | | if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream ) |
| | | break; |
| | | } |
| | | errors_.resize( errorCount ); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Reader::addErrorAndRecover( const std::string &message, |
| | | Token &token, |
| | | TokenType skipUntilToken ) |
| | | { |
| | | addError( message, token ); |
| | | return recoverFromError( skipUntilToken ); |
| | | } |
| | | |
| | | |
| | | Value & |
| | | Reader::currentValue() |
| | | { |
| | | return *(nodes_.top()); |
| | | } |
| | | |
| | | |
| | | Reader::Char |
| | | Reader::getNextChar() |
| | | { |
| | | if ( current_ == end_ ) |
| | | return 0; |
| | | return *current_++; |
| | | } |
| | | |
| | | |
| | | void |
| | | Reader::getLocationLineAndColumn( Location location, |
| | | int &line, |
| | | int &column ) const |
| | | { |
| | | Location current = begin_; |
| | | Location lastLineStart = current; |
| | | line = 0; |
| | | while ( current < location && current != end_ ) |
| | | { |
| | | Char c = *current++; |
| | | if ( c == '\r' ) |
| | | { |
| | | if ( *current == '\n' ) |
| | | ++current; |
| | | lastLineStart = current; |
| | | ++line; |
| | | } |
| | | else if ( c == '\n' ) |
| | | { |
| | | lastLineStart = current; |
| | | ++line; |
| | | } |
| | | } |
| | | // column & line start at 1 |
| | | column = int(location - lastLineStart) + 1; |
| | | ++line; |
| | | } |
| | | |
| | | |
| | | std::string |
| | | Reader::getLocationLineAndColumn( Location location ) const |
| | | { |
| | | int line, column; |
| | | getLocationLineAndColumn( location, line, column ); |
| | | char buffer[18+16+16+1]; |
| | | sprintf( buffer, "Line %d, Column %d", line, column ); |
| | | return buffer; |
| | | } |
| | | |
| | | |
| | | std::string |
| | | Reader::getFormatedErrorMessages() const |
| | | { |
| | | std::string formattedMessage; |
| | | for ( Errors::const_iterator itError = errors_.begin(); |
| | | itError != errors_.end(); |
| | | ++itError ) |
| | | { |
| | | const ErrorInfo &error = *itError; |
| | | formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; |
| | | formattedMessage += " " + error.message_ + "\n"; |
| | | if ( error.extra_ ) |
| | | formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; |
| | | } |
| | | return formattedMessage; |
| | | } |
| | | |
| | | |
| | | std::istream& operator>>( std::istream &sin, Value &root ) |
| | | { |
| | | Json::Reader reader; |
| | | bool ok = reader.parse(sin, root, true); |
| | | //JSON_ASSERT( ok ); |
| | | if (!ok) throw std::runtime_error(reader.getFormatedErrorMessages()); |
| | | return sin; |
| | | } |
| | | |
| | | |
| | | } // namespace Json |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #include <iostream> |
| | | #include <json/value.h> |
| | | #include <json/writer.h> |
| | | #include <utility> |
| | | #include <stdexcept> |
| | | #include <cstring> |
| | | #include <cassert> |
| | | #ifdef JSON_USE_CPPTL |
| | | # include <cpptl/conststring.h> |
| | | #endif |
| | | #include <cstddef> // size_t |
| | | #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR |
| | | # include "json_batchallocator.h" |
| | | #endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR |
| | | |
| | | #define JSON_ASSERT_UNREACHABLE assert( false ) |
| | | #define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw |
| | | #define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) throw std::runtime_error( message ); |
| | | |
| | | namespace Json { |
| | | |
| | | const Value Value::null; |
| | | const Int Value::minInt = Int( ~(UInt(-1)/2) ); |
| | | const Int Value::maxInt = Int( UInt(-1)/2 ); |
| | | const UInt Value::maxUInt = UInt(-1); |
| | | |
| | | // A "safe" implementation of strdup. Allow null pointer to be passed. |
| | | // Also avoid warning on msvc80. |
| | | // |
| | | //inline char *safeStringDup( const char *czstring ) |
| | | //{ |
| | | // if ( czstring ) |
| | | // { |
| | | // const size_t length = (unsigned int)( strlen(czstring) + 1 ); |
| | | // char *newString = static_cast<char *>( malloc( length ) ); |
| | | // memcpy( newString, czstring, length ); |
| | | // return newString; |
| | | // } |
| | | // return 0; |
| | | //} |
| | | // |
| | | //inline char *safeStringDup( const std::string &str ) |
| | | //{ |
| | | // if ( !str.empty() ) |
| | | // { |
| | | // const size_t length = str.length(); |
| | | // char *newString = static_cast<char *>( malloc( length + 1 ) ); |
| | | // memcpy( newString, str.c_str(), length ); |
| | | // newString[length] = 0; |
| | | // return newString; |
| | | // } |
| | | // return 0; |
| | | //} |
| | | |
| | | ValueAllocator::~ValueAllocator() |
| | | { |
| | | } |
| | | |
| | | class DefaultValueAllocator : public ValueAllocator |
| | | { |
| | | public: |
| | | virtual ~DefaultValueAllocator() |
| | | { |
| | | } |
| | | |
| | | virtual char *makeMemberName( const char *memberName ) |
| | | { |
| | | return duplicateStringValue( memberName ); |
| | | } |
| | | |
| | | virtual void releaseMemberName( char *memberName ) |
| | | { |
| | | releaseStringValue( memberName ); |
| | | } |
| | | |
| | | virtual char *duplicateStringValue( const char *value, |
| | | unsigned int length = unknown ) |
| | | { |
| | | //@todo invesgate this old optimization |
| | | //if ( !value || value[0] == 0 ) |
| | | // return 0; |
| | | |
| | | if ( length == unknown ) |
| | | length = (unsigned int)strlen(value); |
| | | char *newString = static_cast<char *>( malloc( length + 1 ) ); |
| | | memcpy( newString, value, length ); |
| | | newString[length] = 0; |
| | | return newString; |
| | | } |
| | | |
| | | virtual void releaseStringValue( char *value ) |
| | | { |
| | | if ( value ) |
| | | free( value ); |
| | | } |
| | | }; |
| | | |
| | | static ValueAllocator *&valueAllocator() |
| | | { |
| | | static DefaultValueAllocator defaultAllocator; |
| | | static ValueAllocator *valueAllocator = &defaultAllocator; |
| | | return valueAllocator; |
| | | } |
| | | |
| | | static struct DummyValueAllocatorInitializer { |
| | | DummyValueAllocatorInitializer() |
| | | { |
| | | valueAllocator(); // ensure valueAllocator() statics are initialized before main(). |
| | | } |
| | | } dummyValueAllocatorInitializer; |
| | | |
| | | |
| | | |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ValueInternals... |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | #ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | # include "json_internalarray.inl" |
| | | # include "json_internalmap.inl" |
| | | #endif // JSON_VALUE_USE_INTERNAL_MAP |
| | | |
| | | # include "json_valueiterator.inl" |
| | | |
| | | |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // class Value::CommentInfo |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | |
| | | |
| | | Value::CommentInfo::CommentInfo() |
| | | : comment_( 0 ) |
| | | { |
| | | } |
| | | |
| | | Value::CommentInfo::~CommentInfo() |
| | | { |
| | | if ( comment_ ) |
| | | valueAllocator()->releaseStringValue( comment_ ); |
| | | } |
| | | |
| | | |
| | | void |
| | | Value::CommentInfo::setComment( const char *text ) |
| | | { |
| | | if ( comment_ ) |
| | | valueAllocator()->releaseStringValue( comment_ ); |
| | | JSON_ASSERT( text ); |
| | | JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /"); |
| | | // It seems that /**/ style comments are acceptable as well. |
| | | comment_ = valueAllocator()->duplicateStringValue( text ); |
| | | } |
| | | |
| | | |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // class Value::CZString |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | # ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | |
| | | // Notes: index_ indicates if the string was allocated when |
| | | // a string is stored. |
| | | |
| | | Value::CZString::CZString( int index ) |
| | | : cstr_( 0 ) |
| | | , index_( index ) |
| | | { |
| | | } |
| | | |
| | | Value::CZString::CZString( const char *cstr, DuplicationPolicy allocate ) |
| | | : cstr_( allocate == duplicate ? valueAllocator()->makeMemberName(cstr) |
| | | : cstr ) |
| | | , index_( allocate ) |
| | | { |
| | | } |
| | | |
| | | Value::CZString::CZString( const CZString &other ) |
| | | : cstr_( other.index_ != noDuplication && other.cstr_ != 0 |
| | | ? valueAllocator()->makeMemberName( other.cstr_ ) |
| | | : other.cstr_ ) |
| | | , index_( other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) |
| | | : other.index_ ) |
| | | { |
| | | } |
| | | |
| | | Value::CZString::~CZString() |
| | | { |
| | | if ( cstr_ && index_ == duplicate ) |
| | | valueAllocator()->releaseMemberName( const_cast<char *>( cstr_ ) ); |
| | | } |
| | | |
| | | void |
| | | Value::CZString::swap( CZString &other ) |
| | | { |
| | | std::swap( cstr_, other.cstr_ ); |
| | | std::swap( index_, other.index_ ); |
| | | } |
| | | |
| | | Value::CZString & |
| | | Value::CZString::operator =( const CZString &other ) |
| | | { |
| | | CZString temp( other ); |
| | | swap( temp ); |
| | | return *this; |
| | | } |
| | | |
| | | bool |
| | | Value::CZString::operator<( const CZString &other ) const |
| | | { |
| | | if ( cstr_ ) |
| | | return strcmp( cstr_, other.cstr_ ) < 0; |
| | | return index_ < other.index_; |
| | | } |
| | | |
| | | bool |
| | | Value::CZString::operator==( const CZString &other ) const |
| | | { |
| | | if ( cstr_ ) |
| | | return strcmp( cstr_, other.cstr_ ) == 0; |
| | | return index_ == other.index_; |
| | | } |
| | | |
| | | |
| | | int |
| | | Value::CZString::index() const |
| | | { |
| | | return index_; |
| | | } |
| | | |
| | | |
| | | const char * |
| | | Value::CZString::c_str() const |
| | | { |
| | | return cstr_; |
| | | } |
| | | |
| | | bool |
| | | Value::CZString::isStaticString() const |
| | | { |
| | | return index_ == noDuplication; |
| | | } |
| | | |
| | | #endif // ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | |
| | | |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // class Value::Value |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | |
| | | /*! \internal Default constructor initialization must be equivalent to: |
| | | * memset( this, 0, sizeof(Value) ) |
| | | * This optimization is used in ValueInternalMap fast allocator. |
| | | */ |
| | | Value::Value( ValueType type ) |
| | | : type_( type ) |
| | | , allocated_( 0 ) |
| | | , comments_( 0 ) |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | , itemIsUsed_( 0 ) |
| | | #endif |
| | | { |
| | | switch ( type ) |
| | | { |
| | | case nullValue: |
| | | break; |
| | | case intValue: |
| | | case uintValue: |
| | | value_.int_ = 0; |
| | | break; |
| | | case realValue: |
| | | value_.real_ = 0.0; |
| | | break; |
| | | case stringValue: |
| | | value_.string_ = 0; |
| | | break; |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | case arrayValue: |
| | | case objectValue: |
| | | value_.map_ = new ObjectValues(); |
| | | break; |
| | | #else |
| | | case arrayValue: |
| | | value_.array_ = arrayAllocator()->newArray(); |
| | | break; |
| | | case objectValue: |
| | | value_.map_ = mapAllocator()->newMap(); |
| | | break; |
| | | #endif |
| | | case booleanValue: |
| | | value_.bool_ = false; |
| | | break; |
| | | default: |
| | | JSON_ASSERT_UNREACHABLE; |
| | | } |
| | | } |
| | | |
| | | |
| | | Value::Value( Int value ) |
| | | : type_( intValue ) |
| | | , comments_( 0 ) |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | , itemIsUsed_( 0 ) |
| | | #endif |
| | | { |
| | | value_.int_ = value; |
| | | } |
| | | |
| | | |
| | | Value::Value( UInt value ) |
| | | : type_( uintValue ) |
| | | , comments_( 0 ) |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | , itemIsUsed_( 0 ) |
| | | #endif |
| | | { |
| | | value_.uint_ = value; |
| | | } |
| | | |
| | | Value::Value( double value ) |
| | | : type_( realValue ) |
| | | , comments_( 0 ) |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | , itemIsUsed_( 0 ) |
| | | #endif |
| | | { |
| | | value_.real_ = value; |
| | | } |
| | | |
| | | Value::Value( const char *value ) |
| | | : type_( stringValue ) |
| | | , allocated_( true ) |
| | | , comments_( 0 ) |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | , itemIsUsed_( 0 ) |
| | | #endif |
| | | { |
| | | value_.string_ = valueAllocator()->duplicateStringValue( value ); |
| | | } |
| | | |
| | | |
| | | Value::Value( const char *beginValue, |
| | | const char *endValue ) |
| | | : type_( stringValue ) |
| | | , allocated_( true ) |
| | | , comments_( 0 ) |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | , itemIsUsed_( 0 ) |
| | | #endif |
| | | { |
| | | value_.string_ = valueAllocator()->duplicateStringValue( beginValue, |
| | | UInt(endValue - beginValue) ); |
| | | } |
| | | |
| | | |
| | | Value::Value( const std::string &value ) |
| | | : type_( stringValue ) |
| | | , allocated_( true ) |
| | | , comments_( 0 ) |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | , itemIsUsed_( 0 ) |
| | | #endif |
| | | { |
| | | value_.string_ = valueAllocator()->duplicateStringValue( value.c_str(), |
| | | (unsigned int)value.length() ); |
| | | |
| | | } |
| | | |
| | | Value::Value( const StaticString &value ) |
| | | : type_( stringValue ) |
| | | , allocated_( false ) |
| | | , comments_( 0 ) |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | , itemIsUsed_( 0 ) |
| | | #endif |
| | | { |
| | | value_.string_ = const_cast<char *>( value.c_str() ); |
| | | } |
| | | |
| | | |
| | | # ifdef JSON_USE_CPPTL |
| | | Value::Value( const CppTL::ConstString &value ) |
| | | : type_( stringValue ) |
| | | , allocated_( true ) |
| | | , comments_( 0 ) |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | , itemIsUsed_( 0 ) |
| | | #endif |
| | | { |
| | | value_.string_ = valueAllocator()->duplicateStringValue( value, value.length() ); |
| | | } |
| | | # endif |
| | | |
| | | Value::Value( bool value ) |
| | | : type_( booleanValue ) |
| | | , comments_( 0 ) |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | , itemIsUsed_( 0 ) |
| | | #endif |
| | | { |
| | | value_.bool_ = value; |
| | | } |
| | | |
| | | |
| | | Value::Value( const Value &other ) |
| | | : type_( other.type_ ) |
| | | , comments_( 0 ) |
| | | # ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | , itemIsUsed_( 0 ) |
| | | #endif |
| | | { |
| | | switch ( type_ ) |
| | | { |
| | | case nullValue: |
| | | case intValue: |
| | | case uintValue: |
| | | case realValue: |
| | | case booleanValue: |
| | | value_ = other.value_; |
| | | break; |
| | | case stringValue: |
| | | if ( other.value_.string_ ) |
| | | { |
| | | value_.string_ = valueAllocator()->duplicateStringValue( other.value_.string_ ); |
| | | allocated_ = true; |
| | | } |
| | | else |
| | | value_.string_ = 0; |
| | | break; |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | case arrayValue: |
| | | case objectValue: |
| | | value_.map_ = new ObjectValues( *other.value_.map_ ); |
| | | break; |
| | | #else |
| | | case arrayValue: |
| | | value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ ); |
| | | break; |
| | | case objectValue: |
| | | value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ ); |
| | | break; |
| | | #endif |
| | | default: |
| | | JSON_ASSERT_UNREACHABLE; |
| | | } |
| | | if ( other.comments_ ) |
| | | { |
| | | comments_ = new CommentInfo[numberOfCommentPlacement]; |
| | | for ( int comment =0; comment < numberOfCommentPlacement; ++comment ) |
| | | { |
| | | const CommentInfo &otherComment = other.comments_[comment]; |
| | | if ( otherComment.comment_ ) |
| | | comments_[comment].setComment( otherComment.comment_ ); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | Value::~Value() |
| | | { |
| | | switch ( type_ ) |
| | | { |
| | | case nullValue: |
| | | case intValue: |
| | | case uintValue: |
| | | case realValue: |
| | | case booleanValue: |
| | | break; |
| | | case stringValue: |
| | | if ( allocated_ ) |
| | | valueAllocator()->releaseStringValue( value_.string_ ); |
| | | break; |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | case arrayValue: |
| | | case objectValue: |
| | | delete value_.map_; |
| | | break; |
| | | #else |
| | | case arrayValue: |
| | | arrayAllocator()->destructArray( value_.array_ ); |
| | | break; |
| | | case objectValue: |
| | | mapAllocator()->destructMap( value_.map_ ); |
| | | break; |
| | | #endif |
| | | default: |
| | | JSON_ASSERT_UNREACHABLE; |
| | | } |
| | | |
| | | if ( comments_ ) |
| | | delete[] comments_; |
| | | } |
| | | |
| | | Value & |
| | | Value::operator=( const Value &other ) |
| | | { |
| | | Value temp( other ); |
| | | swap( temp ); |
| | | return *this; |
| | | } |
| | | |
| | | void |
| | | Value::swap( Value &other ) |
| | | { |
| | | ValueType temp = type_; |
| | | type_ = other.type_; |
| | | other.type_ = temp; |
| | | std::swap( value_, other.value_ ); |
| | | int temp2 = allocated_; |
| | | allocated_ = other.allocated_; |
| | | other.allocated_ = temp2; |
| | | } |
| | | |
| | | ValueType |
| | | Value::type() const |
| | | { |
| | | return type_; |
| | | } |
| | | |
| | | |
| | | int |
| | | Value::compare( const Value &other ) |
| | | { |
| | | /* |
| | | int typeDelta = other.type_ - type_; |
| | | switch ( type_ ) |
| | | { |
| | | case nullValue: |
| | | |
| | | return other.type_ == type_; |
| | | case intValue: |
| | | if ( other.type_.isNumeric() |
| | | case uintValue: |
| | | case realValue: |
| | | case booleanValue: |
| | | break; |
| | | case stringValue, |
| | | break; |
| | | case arrayValue: |
| | | delete value_.array_; |
| | | break; |
| | | case objectValue: |
| | | delete value_.map_; |
| | | default: |
| | | JSON_ASSERT_UNREACHABLE; |
| | | } |
| | | */ |
| | | return 0; // unreachable |
| | | } |
| | | |
| | | bool |
| | | Value::operator <( const Value &other ) const |
| | | { |
| | | int typeDelta = type_ - other.type_; |
| | | if ( typeDelta ) |
| | | return typeDelta < 0 ? true : false; |
| | | switch ( type_ ) |
| | | { |
| | | case nullValue: |
| | | return false; |
| | | case intValue: |
| | | return value_.int_ < other.value_.int_; |
| | | case uintValue: |
| | | return value_.uint_ < other.value_.uint_; |
| | | case realValue: |
| | | return value_.real_ < other.value_.real_; |
| | | case booleanValue: |
| | | return value_.bool_ < other.value_.bool_; |
| | | case stringValue: |
| | | return ( value_.string_ == 0 && other.value_.string_ ) |
| | | || ( other.value_.string_ |
| | | && value_.string_ |
| | | && strcmp( value_.string_, other.value_.string_ ) < 0 ); |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | case arrayValue: |
| | | case objectValue: |
| | | { |
| | | int delta = int( value_.map_->size() - other.value_.map_->size() ); |
| | | if ( delta ) |
| | | return delta < 0; |
| | | return (*value_.map_) < (*other.value_.map_); |
| | | } |
| | | #else |
| | | case arrayValue: |
| | | return value_.array_->compare( *(other.value_.array_) ) < 0; |
| | | case objectValue: |
| | | return value_.map_->compare( *(other.value_.map_) ) < 0; |
| | | #endif |
| | | default: |
| | | JSON_ASSERT_UNREACHABLE; |
| | | } |
| | | return 0; // unreachable |
| | | } |
| | | |
| | | bool |
| | | Value::operator <=( const Value &other ) const |
| | | { |
| | | return !(other > *this); |
| | | } |
| | | |
| | | bool |
| | | Value::operator >=( const Value &other ) const |
| | | { |
| | | return !(*this < other); |
| | | } |
| | | |
| | | bool |
| | | Value::operator >( const Value &other ) const |
| | | { |
| | | return other < *this; |
| | | } |
| | | |
| | | bool |
| | | Value::operator ==( const Value &other ) const |
| | | { |
| | | //if ( type_ != other.type_ ) |
| | | // GCC 2.95.3 says: |
| | | // attempt to take address of bit-field structure member `Json::Value::type_' |
| | | // Beats me, but a temp solves the problem. |
| | | int temp = other.type_; |
| | | if ( type_ != temp ) |
| | | return false; |
| | | switch ( type_ ) |
| | | { |
| | | case nullValue: |
| | | return true; |
| | | case intValue: |
| | | return value_.int_ == other.value_.int_; |
| | | case uintValue: |
| | | return value_.uint_ == other.value_.uint_; |
| | | case realValue: |
| | | return value_.real_ == other.value_.real_; |
| | | case booleanValue: |
| | | return value_.bool_ == other.value_.bool_; |
| | | case stringValue: |
| | | return ( value_.string_ == other.value_.string_ ) |
| | | || ( other.value_.string_ |
| | | && value_.string_ |
| | | && strcmp( value_.string_, other.value_.string_ ) == 0 ); |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | case arrayValue: |
| | | case objectValue: |
| | | return value_.map_->size() == other.value_.map_->size() |
| | | && (*value_.map_) == (*other.value_.map_); |
| | | #else |
| | | case arrayValue: |
| | | return value_.array_->compare( *(other.value_.array_) ) == 0; |
| | | case objectValue: |
| | | return value_.map_->compare( *(other.value_.map_) ) == 0; |
| | | #endif |
| | | default: |
| | | JSON_ASSERT_UNREACHABLE; |
| | | } |
| | | return 0; // unreachable |
| | | } |
| | | |
| | | bool |
| | | Value::operator !=( const Value &other ) const |
| | | { |
| | | return !( *this == other ); |
| | | } |
| | | |
| | | const char * |
| | | Value::asCString() const |
| | | { |
| | | JSON_ASSERT( type_ == stringValue ); |
| | | return value_.string_; |
| | | } |
| | | |
| | | |
| | | std::string |
| | | Value::asString() const |
| | | { |
| | | switch ( type_ ) |
| | | { |
| | | case nullValue: |
| | | return ""; |
| | | case stringValue: |
| | | return value_.string_ ? value_.string_ : ""; |
| | | case booleanValue: |
| | | return value_.bool_ ? "true" : "false"; |
| | | case intValue: |
| | | case uintValue: |
| | | case realValue: |
| | | case arrayValue: |
| | | case objectValue: |
| | | JSON_ASSERT_MESSAGE( false, "Type is not convertible to string" ); |
| | | default: |
| | | JSON_ASSERT_UNREACHABLE; |
| | | } |
| | | return ""; // unreachable |
| | | } |
| | | |
| | | # ifdef JSON_USE_CPPTL |
| | | CppTL::ConstString |
| | | Value::asConstString() const |
| | | { |
| | | return CppTL::ConstString( asString().c_str() ); |
| | | } |
| | | # endif |
| | | |
| | | Value::Int |
| | | Value::asInt() const |
| | | { |
| | | switch ( type_ ) |
| | | { |
| | | case nullValue: |
| | | return 0; |
| | | case intValue: |
| | | return value_.int_; |
| | | case uintValue: |
| | | JSON_ASSERT_MESSAGE( value_.uint_ < (unsigned)maxInt, "integer out of signed integer range" ); |
| | | return value_.uint_; |
| | | case realValue: |
| | | JSON_ASSERT_MESSAGE( value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range" ); |
| | | return Int( value_.real_ ); |
| | | case booleanValue: |
| | | return value_.bool_ ? 1 : 0; |
| | | case stringValue: |
| | | case arrayValue: |
| | | case objectValue: |
| | | JSON_ASSERT_MESSAGE( false, "Type is not convertible to int" ); |
| | | default: |
| | | JSON_ASSERT_UNREACHABLE; |
| | | } |
| | | return 0; // unreachable; |
| | | } |
| | | |
| | | Value::UInt |
| | | Value::asUInt() const |
| | | { |
| | | switch ( type_ ) |
| | | { |
| | | case nullValue: |
| | | return 0; |
| | | case intValue: |
| | | JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to unsigned integer" ); |
| | | return value_.int_; |
| | | case uintValue: |
| | | return value_.uint_; |
| | | case realValue: |
| | | JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range" ); |
| | | return UInt( value_.real_ ); |
| | | case booleanValue: |
| | | return value_.bool_ ? 1 : 0; |
| | | case stringValue: |
| | | case arrayValue: |
| | | case objectValue: |
| | | JSON_ASSERT_MESSAGE( false, "Type is not convertible to uint" ); |
| | | default: |
| | | JSON_ASSERT_UNREACHABLE; |
| | | } |
| | | return 0; // unreachable; |
| | | } |
| | | |
| | | double |
| | | Value::asDouble() const |
| | | { |
| | | switch ( type_ ) |
| | | { |
| | | case nullValue: |
| | | return 0.0; |
| | | case intValue: |
| | | return value_.int_; |
| | | case uintValue: |
| | | return value_.uint_; |
| | | case realValue: |
| | | return value_.real_; |
| | | case booleanValue: |
| | | return value_.bool_ ? 1.0 : 0.0; |
| | | case stringValue: |
| | | case arrayValue: |
| | | case objectValue: |
| | | JSON_ASSERT_MESSAGE( false, "Type is not convertible to double" ); |
| | | default: |
| | | JSON_ASSERT_UNREACHABLE; |
| | | } |
| | | return 0; // unreachable; |
| | | } |
| | | |
| | | bool |
| | | Value::asBool() const |
| | | { |
| | | switch ( type_ ) |
| | | { |
| | | case nullValue: |
| | | return false; |
| | | case intValue: |
| | | case uintValue: |
| | | return value_.int_ != 0; |
| | | case realValue: |
| | | return value_.real_ != 0.0; |
| | | case booleanValue: |
| | | return value_.bool_; |
| | | case stringValue: |
| | | return value_.string_ && value_.string_[0] != 0; |
| | | case arrayValue: |
| | | case objectValue: |
| | | return value_.map_->size() != 0; |
| | | default: |
| | | JSON_ASSERT_UNREACHABLE; |
| | | } |
| | | return false; // unreachable; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::isConvertibleTo( ValueType other ) const |
| | | { |
| | | switch ( type_ ) |
| | | { |
| | | case nullValue: |
| | | return true; |
| | | case intValue: |
| | | return ( other == nullValue && value_.int_ == 0 ) |
| | | || other == intValue |
| | | || ( other == uintValue && value_.int_ >= 0 ) |
| | | || other == realValue |
| | | || other == stringValue |
| | | || other == booleanValue; |
| | | case uintValue: |
| | | return ( other == nullValue && value_.uint_ == 0 ) |
| | | || ( other == intValue && value_.uint_ <= (unsigned)maxInt ) |
| | | || other == uintValue |
| | | || other == realValue |
| | | || other == stringValue |
| | | || other == booleanValue; |
| | | case realValue: |
| | | return ( other == nullValue && value_.real_ == 0.0 ) |
| | | || ( other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt ) |
| | | || ( other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt ) |
| | | || other == realValue |
| | | || other == stringValue |
| | | || other == booleanValue; |
| | | case booleanValue: |
| | | return ( other == nullValue && value_.bool_ == false ) |
| | | || other == intValue |
| | | || other == uintValue |
| | | || other == realValue |
| | | || other == stringValue |
| | | || other == booleanValue; |
| | | case stringValue: |
| | | return other == stringValue |
| | | || ( other == nullValue && (!value_.string_ || value_.string_[0] == 0) ); |
| | | case arrayValue: |
| | | return other == arrayValue |
| | | || ( other == nullValue && value_.map_->size() == 0 ); |
| | | case objectValue: |
| | | return other == objectValue |
| | | || ( other == nullValue && value_.map_->size() == 0 ); |
| | | default: |
| | | JSON_ASSERT_UNREACHABLE; |
| | | } |
| | | return false; // unreachable; |
| | | } |
| | | |
| | | |
| | | /// Number of values in array or object |
| | | Value::UInt |
| | | Value::size() const |
| | | { |
| | | switch ( type_ ) |
| | | { |
| | | case nullValue: |
| | | case intValue: |
| | | case uintValue: |
| | | case realValue: |
| | | case booleanValue: |
| | | case stringValue: |
| | | return 0; |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | case arrayValue: // size of the array is highest index + 1 |
| | | if ( !value_.map_->empty() ) |
| | | { |
| | | ObjectValues::const_iterator itLast = value_.map_->end(); |
| | | --itLast; |
| | | return (*itLast).first.index()+1; |
| | | } |
| | | return 0; |
| | | case objectValue: |
| | | return Int( value_.map_->size() ); |
| | | #else |
| | | case arrayValue: |
| | | return Int( value_.array_->size() ); |
| | | case objectValue: |
| | | return Int( value_.map_->size() ); |
| | | #endif |
| | | default: |
| | | JSON_ASSERT_UNREACHABLE; |
| | | } |
| | | return 0; // unreachable; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::empty() const |
| | | { |
| | | if ( isNull() || isArray() || isObject() ) |
| | | return size() == 0u; |
| | | else |
| | | return false; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::operator!() const |
| | | { |
| | | return isNull(); |
| | | } |
| | | |
| | | |
| | | void |
| | | Value::clear() |
| | | { |
| | | JSON_ASSERT( type_ == nullValue || type_ == arrayValue || type_ == objectValue ); |
| | | |
| | | switch ( type_ ) |
| | | { |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | case arrayValue: |
| | | case objectValue: |
| | | value_.map_->clear(); |
| | | break; |
| | | #else |
| | | case arrayValue: |
| | | value_.array_->clear(); |
| | | break; |
| | | case objectValue: |
| | | value_.map_->clear(); |
| | | break; |
| | | #endif |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | void |
| | | Value::resize( UInt newSize ) |
| | | { |
| | | JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); |
| | | if ( type_ == nullValue ) |
| | | *this = Value( arrayValue ); |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | UInt oldSize = size(); |
| | | if ( newSize == 0 ) |
| | | clear(); |
| | | else if ( newSize > oldSize ) |
| | | (*this)[ newSize - 1 ]; |
| | | else |
| | | { |
| | | for ( UInt index = newSize; index < oldSize; ++index ) |
| | | value_.map_->erase( index ); |
| | | assert( size() == newSize ); |
| | | } |
| | | #else |
| | | value_.array_->resize( newSize ); |
| | | #endif |
| | | } |
| | | |
| | | |
| | | Value & |
| | | Value::operator[]( UInt index ) |
| | | { |
| | | JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); |
| | | if ( type_ == nullValue ) |
| | | *this = Value( arrayValue ); |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | CZString key( index ); |
| | | ObjectValues::iterator it = value_.map_->lower_bound( key ); |
| | | if ( it != value_.map_->end() && (*it).first == key ) |
| | | return (*it).second; |
| | | |
| | | ObjectValues::value_type defaultValue( key, null ); |
| | | it = value_.map_->insert( it, defaultValue ); |
| | | return (*it).second; |
| | | #else |
| | | return value_.array_->resolveReference( index ); |
| | | #endif |
| | | } |
| | | |
| | | |
| | | const Value & |
| | | Value::operator[]( UInt index ) const |
| | | { |
| | | JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); |
| | | if ( type_ == nullValue ) |
| | | return null; |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | CZString key( index ); |
| | | ObjectValues::const_iterator it = value_.map_->find( key ); |
| | | if ( it == value_.map_->end() ) |
| | | return null; |
| | | return (*it).second; |
| | | #else |
| | | Value *value = value_.array_->find( index ); |
| | | return value ? *value : null; |
| | | #endif |
| | | } |
| | | |
| | | |
| | | Value & |
| | | Value::operator[]( const char *key ) |
| | | { |
| | | return resolveReference( key, false ); |
| | | } |
| | | |
| | | |
| | | Value & |
| | | Value::resolveReference( const char *key, |
| | | bool isStatic ) |
| | | { |
| | | JSON_ASSERT( type_ == nullValue || type_ == objectValue ); |
| | | if ( type_ == nullValue ) |
| | | *this = Value( objectValue ); |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | CZString actualKey( key, isStatic ? CZString::noDuplication |
| | | : CZString::duplicateOnCopy ); |
| | | ObjectValues::iterator it = value_.map_->lower_bound( actualKey ); |
| | | if ( it != value_.map_->end() && (*it).first == actualKey ) |
| | | return (*it).second; |
| | | |
| | | ObjectValues::value_type defaultValue( actualKey, null ); |
| | | it = value_.map_->insert( it, defaultValue ); |
| | | Value &value = (*it).second; |
| | | return value; |
| | | #else |
| | | return value_.map_->resolveReference( key, isStatic ); |
| | | #endif |
| | | } |
| | | |
| | | |
| | | Value |
| | | Value::get( UInt index, |
| | | const Value &defaultValue ) const |
| | | { |
| | | const Value *value = &((*this)[index]); |
| | | return value == &null ? defaultValue : *value; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::isValidIndex( UInt index ) const |
| | | { |
| | | return index < size(); |
| | | } |
| | | |
| | | |
| | | |
| | | const Value & |
| | | Value::operator[]( const char *key ) const |
| | | { |
| | | JSON_ASSERT( type_ == nullValue || type_ == objectValue ); |
| | | if ( type_ == nullValue ) |
| | | return null; |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | CZString actualKey( key, CZString::noDuplication ); |
| | | ObjectValues::const_iterator it = value_.map_->find( actualKey ); |
| | | if ( it == value_.map_->end() ) |
| | | return null; |
| | | return (*it).second; |
| | | #else |
| | | const Value *value = value_.map_->find( key ); |
| | | return value ? *value : null; |
| | | #endif |
| | | } |
| | | |
| | | |
| | | Value & |
| | | Value::operator[]( const std::string &key ) |
| | | { |
| | | return (*this)[ key.c_str() ]; |
| | | } |
| | | |
| | | |
| | | const Value & |
| | | Value::operator[]( const std::string &key ) const |
| | | { |
| | | return (*this)[ key.c_str() ]; |
| | | } |
| | | |
| | | Value & |
| | | Value::operator[]( const StaticString &key ) |
| | | { |
| | | return resolveReference( key, true ); |
| | | } |
| | | |
| | | |
| | | # ifdef JSON_USE_CPPTL |
| | | Value & |
| | | Value::operator[]( const CppTL::ConstString &key ) |
| | | { |
| | | return (*this)[ key.c_str() ]; |
| | | } |
| | | |
| | | |
| | | const Value & |
| | | Value::operator[]( const CppTL::ConstString &key ) const |
| | | { |
| | | return (*this)[ key.c_str() ]; |
| | | } |
| | | # endif |
| | | |
| | | |
| | | Value & |
| | | Value::append( const Value &value ) |
| | | { |
| | | return (*this)[size()] = value; |
| | | } |
| | | |
| | | |
| | | Value |
| | | Value::get( const char *key, |
| | | const Value &defaultValue ) const |
| | | { |
| | | const Value *value = &((*this)[key]); |
| | | return value == &null ? defaultValue : *value; |
| | | } |
| | | |
| | | |
| | | Value |
| | | Value::get( const std::string &key, |
| | | const Value &defaultValue ) const |
| | | { |
| | | return get( key.c_str(), defaultValue ); |
| | | } |
| | | |
| | | Value |
| | | Value::removeMember( const char* key ) |
| | | { |
| | | JSON_ASSERT( type_ == nullValue || type_ == objectValue ); |
| | | if ( type_ == nullValue ) |
| | | return null; |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | CZString actualKey( key, CZString::noDuplication ); |
| | | ObjectValues::iterator it = value_.map_->find( actualKey ); |
| | | if ( it == value_.map_->end() ) |
| | | return null; |
| | | Value old(it->second); |
| | | value_.map_->erase(it); |
| | | return old; |
| | | #else |
| | | Value *value = value_.map_->find( key ); |
| | | if (value){ |
| | | Value old(*value); |
| | | value_.map_.remove( key ); |
| | | return old; |
| | | } else { |
| | | return null; |
| | | } |
| | | #endif |
| | | } |
| | | |
| | | Value |
| | | Value::removeMember( const std::string &key ) |
| | | { |
| | | return removeMember( key.c_str() ); |
| | | } |
| | | |
| | | # ifdef JSON_USE_CPPTL |
| | | Value |
| | | Value::get( const CppTL::ConstString &key, |
| | | const Value &defaultValue ) const |
| | | { |
| | | return get( key.c_str(), defaultValue ); |
| | | } |
| | | # endif |
| | | |
| | | bool |
| | | Value::isMember( const char *key ) const |
| | | { |
| | | const Value *value = &((*this)[key]); |
| | | return value != &null; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::isMember( const std::string &key ) const |
| | | { |
| | | return isMember( key.c_str() ); |
| | | } |
| | | |
| | | |
| | | # ifdef JSON_USE_CPPTL |
| | | bool |
| | | Value::isMember( const CppTL::ConstString &key ) const |
| | | { |
| | | return isMember( key.c_str() ); |
| | | } |
| | | #endif |
| | | |
| | | Value::Members |
| | | Value::getMemberNames() const |
| | | { |
| | | JSON_ASSERT( type_ == nullValue || type_ == objectValue ); |
| | | if ( type_ == nullValue ) |
| | | return Value::Members(); |
| | | Members members; |
| | | members.reserve( value_.map_->size() ); |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | ObjectValues::const_iterator it = value_.map_->begin(); |
| | | ObjectValues::const_iterator itEnd = value_.map_->end(); |
| | | for ( ; it != itEnd; ++it ) |
| | | members.push_back( std::string( (*it).first.c_str() ) ); |
| | | #else |
| | | ValueInternalMap::IteratorState it; |
| | | ValueInternalMap::IteratorState itEnd; |
| | | value_.map_->makeBeginIterator( it ); |
| | | value_.map_->makeEndIterator( itEnd ); |
| | | for ( ; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) ) |
| | | members.push_back( std::string( ValueInternalMap::key( it ) ) ); |
| | | #endif |
| | | return members; |
| | | } |
| | | // |
| | | //# ifdef JSON_USE_CPPTL |
| | | //EnumMemberNames |
| | | //Value::enumMemberNames() const |
| | | //{ |
| | | // if ( type_ == objectValue ) |
| | | // { |
| | | // return CppTL::Enum::any( CppTL::Enum::transform( |
| | | // CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ), |
| | | // MemberNamesTransform() ) ); |
| | | // } |
| | | // return EnumMemberNames(); |
| | | //} |
| | | // |
| | | // |
| | | //EnumValues |
| | | //Value::enumValues() const |
| | | //{ |
| | | // if ( type_ == objectValue || type_ == arrayValue ) |
| | | // return CppTL::Enum::anyValues( *(value_.map_), |
| | | // CppTL::Type<const Value &>() ); |
| | | // return EnumValues(); |
| | | //} |
| | | // |
| | | //# endif |
| | | |
| | | |
| | | bool |
| | | Value::isNull() const |
| | | { |
| | | return type_ == nullValue; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::isBool() const |
| | | { |
| | | return type_ == booleanValue; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::isInt() const |
| | | { |
| | | return type_ == intValue; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::isUInt() const |
| | | { |
| | | return type_ == uintValue; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::isIntegral() const |
| | | { |
| | | return type_ == intValue |
| | | || type_ == uintValue |
| | | || type_ == booleanValue; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::isDouble() const |
| | | { |
| | | return type_ == realValue; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::isNumeric() const |
| | | { |
| | | return isIntegral() || isDouble(); |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::isString() const |
| | | { |
| | | return type_ == stringValue; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::isArray() const |
| | | { |
| | | return type_ == nullValue || type_ == arrayValue; |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::isObject() const |
| | | { |
| | | return type_ == nullValue || type_ == objectValue; |
| | | } |
| | | |
| | | |
| | | void |
| | | Value::setComment( const char *comment, |
| | | CommentPlacement placement ) |
| | | { |
| | | if ( !comments_ ) |
| | | comments_ = new CommentInfo[numberOfCommentPlacement]; |
| | | comments_[placement].setComment( comment ); |
| | | } |
| | | |
| | | |
| | | void |
| | | Value::setComment( const std::string &comment, |
| | | CommentPlacement placement ) |
| | | { |
| | | setComment( comment.c_str(), placement ); |
| | | } |
| | | |
| | | |
| | | bool |
| | | Value::hasComment( CommentPlacement placement ) const |
| | | { |
| | | return comments_ != 0 && comments_[placement].comment_ != 0; |
| | | } |
| | | |
| | | std::string |
| | | Value::getComment( CommentPlacement placement ) const |
| | | { |
| | | if ( hasComment(placement) ) |
| | | return comments_[placement].comment_; |
| | | return ""; |
| | | } |
| | | |
| | | |
| | | std::string |
| | | Value::toStyledString() const |
| | | { |
| | | StyledWriter writer; |
| | | return writer.write( *this ); |
| | | } |
| | | |
| | | |
| | | Value::const_iterator |
| | | Value::begin() const |
| | | { |
| | | switch ( type_ ) |
| | | { |
| | | #ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | case arrayValue: |
| | | if ( value_.array_ ) |
| | | { |
| | | ValueInternalArray::IteratorState it; |
| | | value_.array_->makeBeginIterator( it ); |
| | | return const_iterator( it ); |
| | | } |
| | | break; |
| | | case objectValue: |
| | | if ( value_.map_ ) |
| | | { |
| | | ValueInternalMap::IteratorState it; |
| | | value_.map_->makeBeginIterator( it ); |
| | | return const_iterator( it ); |
| | | } |
| | | break; |
| | | #else |
| | | case arrayValue: |
| | | case objectValue: |
| | | if ( value_.map_ ) |
| | | return const_iterator( value_.map_->begin() ); |
| | | break; |
| | | #endif |
| | | default: |
| | | break; |
| | | } |
| | | return const_iterator(); |
| | | } |
| | | |
| | | Value::const_iterator |
| | | Value::end() const |
| | | { |
| | | switch ( type_ ) |
| | | { |
| | | #ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | case arrayValue: |
| | | if ( value_.array_ ) |
| | | { |
| | | ValueInternalArray::IteratorState it; |
| | | value_.array_->makeEndIterator( it ); |
| | | return const_iterator( it ); |
| | | } |
| | | break; |
| | | case objectValue: |
| | | if ( value_.map_ ) |
| | | { |
| | | ValueInternalMap::IteratorState it; |
| | | value_.map_->makeEndIterator( it ); |
| | | return const_iterator( it ); |
| | | } |
| | | break; |
| | | #else |
| | | case arrayValue: |
| | | case objectValue: |
| | | if ( value_.map_ ) |
| | | return const_iterator( value_.map_->end() ); |
| | | break; |
| | | #endif |
| | | default: |
| | | break; |
| | | } |
| | | return const_iterator(); |
| | | } |
| | | |
| | | |
| | | Value::iterator |
| | | Value::begin() |
| | | { |
| | | switch ( type_ ) |
| | | { |
| | | #ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | case arrayValue: |
| | | if ( value_.array_ ) |
| | | { |
| | | ValueInternalArray::IteratorState it; |
| | | value_.array_->makeBeginIterator( it ); |
| | | return iterator( it ); |
| | | } |
| | | break; |
| | | case objectValue: |
| | | if ( value_.map_ ) |
| | | { |
| | | ValueInternalMap::IteratorState it; |
| | | value_.map_->makeBeginIterator( it ); |
| | | return iterator( it ); |
| | | } |
| | | break; |
| | | #else |
| | | case arrayValue: |
| | | case objectValue: |
| | | if ( value_.map_ ) |
| | | return iterator( value_.map_->begin() ); |
| | | break; |
| | | #endif |
| | | default: |
| | | break; |
| | | } |
| | | return iterator(); |
| | | } |
| | | |
| | | Value::iterator |
| | | Value::end() |
| | | { |
| | | switch ( type_ ) |
| | | { |
| | | #ifdef JSON_VALUE_USE_INTERNAL_MAP |
| | | case arrayValue: |
| | | if ( value_.array_ ) |
| | | { |
| | | ValueInternalArray::IteratorState it; |
| | | value_.array_->makeEndIterator( it ); |
| | | return iterator( it ); |
| | | } |
| | | break; |
| | | case objectValue: |
| | | if ( value_.map_ ) |
| | | { |
| | | ValueInternalMap::IteratorState it; |
| | | value_.map_->makeEndIterator( it ); |
| | | return iterator( it ); |
| | | } |
| | | break; |
| | | #else |
| | | case arrayValue: |
| | | case objectValue: |
| | | if ( value_.map_ ) |
| | | return iterator( value_.map_->end() ); |
| | | break; |
| | | #endif |
| | | default: |
| | | break; |
| | | } |
| | | return iterator(); |
| | | } |
| | | |
| | | |
| | | // class PathArgument |
| | | // ////////////////////////////////////////////////////////////////// |
| | | |
| | | PathArgument::PathArgument() |
| | | : kind_( kindNone ) |
| | | { |
| | | } |
| | | |
| | | |
| | | PathArgument::PathArgument( Value::UInt index ) |
| | | : index_( index ) |
| | | , kind_( kindIndex ) |
| | | { |
| | | } |
| | | |
| | | |
| | | PathArgument::PathArgument( const char *key ) |
| | | : key_( key ) |
| | | , kind_( kindKey ) |
| | | { |
| | | } |
| | | |
| | | |
| | | PathArgument::PathArgument( const std::string &key ) |
| | | : key_( key.c_str() ) |
| | | , kind_( kindKey ) |
| | | { |
| | | } |
| | | |
| | | // class Path |
| | | // ////////////////////////////////////////////////////////////////// |
| | | |
| | | Path::Path( const std::string &path, |
| | | const PathArgument &a1, |
| | | const PathArgument &a2, |
| | | const PathArgument &a3, |
| | | const PathArgument &a4, |
| | | const PathArgument &a5 ) |
| | | { |
| | | InArgs in; |
| | | in.push_back( &a1 ); |
| | | in.push_back( &a2 ); |
| | | in.push_back( &a3 ); |
| | | in.push_back( &a4 ); |
| | | in.push_back( &a5 ); |
| | | makePath( path, in ); |
| | | } |
| | | |
| | | |
| | | void |
| | | Path::makePath( const std::string &path, |
| | | const InArgs &in ) |
| | | { |
| | | const char *current = path.c_str(); |
| | | const char *end = current + path.length(); |
| | | InArgs::const_iterator itInArg = in.begin(); |
| | | while ( current != end ) |
| | | { |
| | | if ( *current == '[' ) |
| | | { |
| | | ++current; |
| | | if ( *current == '%' ) |
| | | addPathInArg( path, in, itInArg, PathArgument::kindIndex ); |
| | | else |
| | | { |
| | | Value::UInt index = 0; |
| | | for ( ; current != end && *current >= '0' && *current <= '9'; ++current ) |
| | | index = index * 10 + Value::UInt(*current - '0'); |
| | | args_.push_back( index ); |
| | | } |
| | | if ( current == end || *current++ != ']' ) |
| | | invalidPath( path, int(current - path.c_str()) ); |
| | | } |
| | | else if ( *current == '%' ) |
| | | { |
| | | addPathInArg( path, in, itInArg, PathArgument::kindKey ); |
| | | ++current; |
| | | } |
| | | else if ( *current == '.' ) |
| | | { |
| | | ++current; |
| | | } |
| | | else |
| | | { |
| | | const char *beginName = current; |
| | | while ( current != end && !strchr( "[.", *current ) ) |
| | | ++current; |
| | | args_.push_back( std::string( beginName, current ) ); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | void |
| | | Path::addPathInArg( const std::string &path, |
| | | const InArgs &in, |
| | | InArgs::const_iterator &itInArg, |
| | | PathArgument::Kind kind ) |
| | | { |
| | | if ( itInArg == in.end() ) |
| | | { |
| | | // Error: missing argument %d |
| | | } |
| | | else if ( (*itInArg)->kind_ != kind ) |
| | | { |
| | | // Error: bad argument type |
| | | } |
| | | else |
| | | { |
| | | args_.push_back( **itInArg ); |
| | | } |
| | | } |
| | | |
| | | |
| | | void |
| | | Path::invalidPath( const std::string &path, |
| | | int location ) |
| | | { |
| | | // Error: invalid path. |
| | | } |
| | | |
| | | |
| | | const Value & |
| | | Path::resolve( const Value &root ) const |
| | | { |
| | | const Value *node = &root; |
| | | for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) |
| | | { |
| | | const PathArgument &arg = *it; |
| | | if ( arg.kind_ == PathArgument::kindIndex ) |
| | | { |
| | | if ( !node->isArray() || node->isValidIndex( arg.index_ ) ) |
| | | { |
| | | // Error: unable to resolve path (array value expected at position... |
| | | } |
| | | node = &((*node)[arg.index_]); |
| | | } |
| | | else if ( arg.kind_ == PathArgument::kindKey ) |
| | | { |
| | | if ( !node->isObject() ) |
| | | { |
| | | // Error: unable to resolve path (object value expected at position...) |
| | | } |
| | | node = &((*node)[arg.key_]); |
| | | if ( node == &Value::null ) |
| | | { |
| | | // Error: unable to resolve path (object has no member named '' at position...) |
| | | } |
| | | } |
| | | } |
| | | return *node; |
| | | } |
| | | |
| | | |
| | | Value |
| | | Path::resolve( const Value &root, |
| | | const Value &defaultValue ) const |
| | | { |
| | | const Value *node = &root; |
| | | for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) |
| | | { |
| | | const PathArgument &arg = *it; |
| | | if ( arg.kind_ == PathArgument::kindIndex ) |
| | | { |
| | | if ( !node->isArray() || node->isValidIndex( arg.index_ ) ) |
| | | return defaultValue; |
| | | node = &((*node)[arg.index_]); |
| | | } |
| | | else if ( arg.kind_ == PathArgument::kindKey ) |
| | | { |
| | | if ( !node->isObject() ) |
| | | return defaultValue; |
| | | node = &((*node)[arg.key_]); |
| | | if ( node == &Value::null ) |
| | | return defaultValue; |
| | | } |
| | | } |
| | | return *node; |
| | | } |
| | | |
| | | |
| | | Value & |
| | | Path::make( Value &root ) const |
| | | { |
| | | Value *node = &root; |
| | | for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) |
| | | { |
| | | const PathArgument &arg = *it; |
| | | if ( arg.kind_ == PathArgument::kindIndex ) |
| | | { |
| | | if ( !node->isArray() ) |
| | | { |
| | | // Error: node is not an array at position ... |
| | | } |
| | | node = &((*node)[arg.index_]); |
| | | } |
| | | else if ( arg.kind_ == PathArgument::kindKey ) |
| | | { |
| | | if ( !node->isObject() ) |
| | | { |
| | | // Error: node is not an object at position... |
| | | } |
| | | node = &((*node)[arg.key_]); |
| | | } |
| | | } |
| | | return *node; |
| | | } |
| | | |
| | | |
| | | } // namespace Json |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | // included by json_value.cpp |
| | | // everything is within Json namespace |
| | | |
| | | |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // class ValueIteratorBase |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | |
| | | ValueIteratorBase::ValueIteratorBase() |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | : current_() |
| | | , isNull_( true ) |
| | | { |
| | | } |
| | | #else |
| | | : isArray_( true ) |
| | | , isNull_( true ) |
| | | { |
| | | iterator_.array_ = ValueInternalArray::IteratorState(); |
| | | } |
| | | #endif |
| | | |
| | | |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator ¤t ) |
| | | : current_( current ) |
| | | , isNull_( false ) |
| | | { |
| | | } |
| | | #else |
| | | ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state ) |
| | | : isArray_( true ) |
| | | { |
| | | iterator_.array_ = state; |
| | | } |
| | | |
| | | |
| | | ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state ) |
| | | : isArray_( false ) |
| | | { |
| | | iterator_.map_ = state; |
| | | } |
| | | #endif |
| | | |
| | | Value & |
| | | ValueIteratorBase::deref() const |
| | | { |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | return current_->second; |
| | | #else |
| | | if ( isArray_ ) |
| | | return ValueInternalArray::dereference( iterator_.array_ ); |
| | | return ValueInternalMap::value( iterator_.map_ ); |
| | | #endif |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueIteratorBase::increment() |
| | | { |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | ++current_; |
| | | #else |
| | | if ( isArray_ ) |
| | | ValueInternalArray::increment( iterator_.array_ ); |
| | | ValueInternalMap::increment( iterator_.map_ ); |
| | | #endif |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueIteratorBase::decrement() |
| | | { |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | --current_; |
| | | #else |
| | | if ( isArray_ ) |
| | | ValueInternalArray::decrement( iterator_.array_ ); |
| | | ValueInternalMap::decrement( iterator_.map_ ); |
| | | #endif |
| | | } |
| | | |
| | | |
| | | ValueIteratorBase::difference_type |
| | | ValueIteratorBase::computeDistance( const SelfType &other ) const |
| | | { |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | # ifdef JSON_USE_CPPTL_SMALLMAP |
| | | return current_ - other.current_; |
| | | # else |
| | | // Iterator for null value are initialized using the default |
| | | // constructor, which initialize current_ to the default |
| | | // std::map::iterator. As begin() and end() are two instance |
| | | // of the default std::map::iterator, they can not be compared. |
| | | // To allow this, we handle this comparison specifically. |
| | | if ( isNull_ && other.isNull_ ) |
| | | { |
| | | return 0; |
| | | } |
| | | |
| | | |
| | | // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL, |
| | | // which is the one used by default). |
| | | // Using a portable hand-made version for non random iterator instead: |
| | | // return difference_type( std::distance( current_, other.current_ ) ); |
| | | difference_type myDistance = 0; |
| | | for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it ) |
| | | { |
| | | ++myDistance; |
| | | } |
| | | return myDistance; |
| | | # endif |
| | | #else |
| | | if ( isArray_ ) |
| | | return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ ); |
| | | return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ ); |
| | | #endif |
| | | } |
| | | |
| | | |
| | | bool |
| | | ValueIteratorBase::isEqual( const SelfType &other ) const |
| | | { |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | if ( isNull_ ) |
| | | { |
| | | return other.isNull_; |
| | | } |
| | | return current_ == other.current_; |
| | | #else |
| | | if ( isArray_ ) |
| | | return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ ); |
| | | return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ ); |
| | | #endif |
| | | } |
| | | |
| | | |
| | | void |
| | | ValueIteratorBase::copy( const SelfType &other ) |
| | | { |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | current_ = other.current_; |
| | | #else |
| | | if ( isArray_ ) |
| | | iterator_.array_ = other.iterator_.array_; |
| | | iterator_.map_ = other.iterator_.map_; |
| | | #endif |
| | | } |
| | | |
| | | |
| | | Value |
| | | ValueIteratorBase::key() const |
| | | { |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | const Value::CZString czstring = (*current_).first; |
| | | if ( czstring.c_str() ) |
| | | { |
| | | if ( czstring.isStaticString() ) |
| | | return Value( StaticString( czstring.c_str() ) ); |
| | | return Value( czstring.c_str() ); |
| | | } |
| | | return Value( czstring.index() ); |
| | | #else |
| | | if ( isArray_ ) |
| | | return Value( ValueInternalArray::indexOf( iterator_.array_ ) ); |
| | | bool isStatic; |
| | | const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic ); |
| | | if ( isStatic ) |
| | | return Value( StaticString( memberName ) ); |
| | | return Value( memberName ); |
| | | #endif |
| | | } |
| | | |
| | | |
| | | UInt |
| | | ValueIteratorBase::index() const |
| | | { |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | const Value::CZString czstring = (*current_).first; |
| | | if ( !czstring.c_str() ) |
| | | return czstring.index(); |
| | | return Value::UInt( -1 ); |
| | | #else |
| | | if ( isArray_ ) |
| | | return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) ); |
| | | return Value::UInt( -1 ); |
| | | #endif |
| | | } |
| | | |
| | | |
| | | const char * |
| | | ValueIteratorBase::memberName() const |
| | | { |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | const char *name = (*current_).first.c_str(); |
| | | return name ? name : ""; |
| | | #else |
| | | if ( !isArray_ ) |
| | | return ValueInternalMap::key( iterator_.map_ ); |
| | | return ""; |
| | | #endif |
| | | } |
| | | |
| | | |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // class ValueConstIterator |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | |
| | | ValueConstIterator::ValueConstIterator() |
| | | { |
| | | } |
| | | |
| | | |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator ¤t ) |
| | | : ValueIteratorBase( current ) |
| | | { |
| | | } |
| | | #else |
| | | ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state ) |
| | | : ValueIteratorBase( state ) |
| | | { |
| | | } |
| | | |
| | | ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state ) |
| | | : ValueIteratorBase( state ) |
| | | { |
| | | } |
| | | #endif |
| | | |
| | | ValueConstIterator & |
| | | ValueConstIterator::operator =( const ValueIteratorBase &other ) |
| | | { |
| | | copy( other ); |
| | | return *this; |
| | | } |
| | | |
| | | |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // class ValueIterator |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | // ////////////////////////////////////////////////////////////////// |
| | | |
| | | ValueIterator::ValueIterator() |
| | | { |
| | | } |
| | | |
| | | |
| | | #ifndef JSON_VALUE_USE_INTERNAL_MAP |
| | | ValueIterator::ValueIterator( const Value::ObjectValues::iterator ¤t ) |
| | | : ValueIteratorBase( current ) |
| | | { |
| | | } |
| | | #else |
| | | ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state ) |
| | | : ValueIteratorBase( state ) |
| | | { |
| | | } |
| | | |
| | | ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state ) |
| | | : ValueIteratorBase( state ) |
| | | { |
| | | } |
| | | #endif |
| | | |
| | | ValueIterator::ValueIterator( const ValueConstIterator &other ) |
| | | : ValueIteratorBase( other ) |
| | | { |
| | | } |
| | | |
| | | ValueIterator::ValueIterator( const ValueIterator &other ) |
| | | : ValueIteratorBase( other ) |
| | | { |
| | | } |
| | | |
| | | ValueIterator & |
| | | ValueIterator::operator =( const SelfType &other ) |
| | | { |
| | | copy( other ); |
| | | return *this; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #include <json/writer.h> |
| | | #include <utility> |
| | | #include <assert.h> |
| | | #include <stdio.h> |
| | | #include <string.h> |
| | | #include <iostream> |
| | | #include <sstream> |
| | | #include <iomanip> |
| | | |
| | | #if _MSC_VER >= 1400 // VC++ 8.0 |
| | | #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. |
| | | #endif |
| | | |
| | | namespace Json { |
| | | |
| | | static bool isControlCharacter(char ch) |
| | | { |
| | | return ch > 0 && ch <= 0x1F; |
| | | } |
| | | |
| | | static bool containsControlCharacter( const char* str ) |
| | | { |
| | | while ( *str ) |
| | | { |
| | | if ( isControlCharacter( *(str++) ) ) |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | static void uintToString( unsigned int value, |
| | | char *¤t ) |
| | | { |
| | | *--current = 0; |
| | | do |
| | | { |
| | | *--current = (value % 10) + '0'; |
| | | value /= 10; |
| | | } |
| | | while ( value != 0 ); |
| | | } |
| | | |
| | | std::string valueToString( Int value ) |
| | | { |
| | | char buffer[32]; |
| | | char *current = buffer + sizeof(buffer); |
| | | bool isNegative = value < 0; |
| | | if ( isNegative ) |
| | | value = -value; |
| | | uintToString( UInt(value), current ); |
| | | if ( isNegative ) |
| | | *--current = '-'; |
| | | assert( current >= buffer ); |
| | | return current; |
| | | } |
| | | |
| | | |
| | | std::string valueToString( UInt value ) |
| | | { |
| | | char buffer[32]; |
| | | char *current = buffer + sizeof(buffer); |
| | | uintToString( value, current ); |
| | | assert( current >= buffer ); |
| | | return current; |
| | | } |
| | | |
| | | std::string valueToString( double value ) |
| | | { |
| | | char buffer[32]; |
| | | #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. |
| | | sprintf_s(buffer, sizeof(buffer), "%#.16g", value); |
| | | #else |
| | | sprintf(buffer, "%#.16g", value); |
| | | #endif |
| | | char* ch = buffer + strlen(buffer) - 1; |
| | | if (*ch != '0') return buffer; // nothing to truncate, so save time |
| | | while(ch > buffer && *ch == '0'){ |
| | | --ch; |
| | | } |
| | | char* last_nonzero = ch; |
| | | while(ch >= buffer){ |
| | | switch(*ch){ |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | case '6': |
| | | case '7': |
| | | case '8': |
| | | case '9': |
| | | --ch; |
| | | continue; |
| | | case '.': |
| | | // Truncate zeroes to save bytes in output, but keep one. |
| | | *(last_nonzero+2) = '\0'; |
| | | return buffer; |
| | | default: |
| | | return buffer; |
| | | } |
| | | } |
| | | return buffer; |
| | | } |
| | | |
| | | |
| | | std::string valueToString( bool value ) |
| | | { |
| | | return value ? "true" : "false"; |
| | | } |
| | | |
| | | std::string valueToQuotedString( const char *value ) |
| | | { |
| | | // Not sure how to handle unicode... |
| | | if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value )) |
| | | return std::string("\"") + value + "\""; |
| | | // We have to walk value and escape any special characters. |
| | | // Appending to std::string is not efficient, but this should be rare. |
| | | // (Note: forward slashes are *not* rare, but I am not escaping them.) |
| | | unsigned maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL |
| | | std::string result; |
| | | result.reserve(maxsize); // to avoid lots of mallocs |
| | | result += "\""; |
| | | for (const char* c=value; *c != 0; ++c) |
| | | { |
| | | switch(*c) |
| | | { |
| | | case '\"': |
| | | result += "\\\""; |
| | | break; |
| | | case '\\': |
| | | result += "\\\\"; |
| | | break; |
| | | case '\b': |
| | | result += "\\b"; |
| | | break; |
| | | case '\f': |
| | | result += "\\f"; |
| | | break; |
| | | case '\n': |
| | | result += "\\n"; |
| | | break; |
| | | case '\r': |
| | | result += "\\r"; |
| | | break; |
| | | case '\t': |
| | | result += "\\t"; |
| | | break; |
| | | //case '/': |
| | | // Even though \/ is considered a legal escape in JSON, a bare |
| | | // slash is also legal, so I see no reason to escape it. |
| | | // (I hope I am not misunderstanding something. |
| | | // blep notes: actually escaping \/ may be useful in javascript to avoid </ |
| | | // sequence. |
| | | // Should add a flag to allow this compatibility mode and prevent this |
| | | // sequence from occurring. |
| | | default: |
| | | if ( isControlCharacter( *c ) ) |
| | | { |
| | | std::ostringstream oss; |
| | | oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c); |
| | | result += oss.str(); |
| | | } |
| | | else |
| | | { |
| | | result += *c; |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | result += "\""; |
| | | return result; |
| | | } |
| | | |
| | | // Class Writer |
| | | // ////////////////////////////////////////////////////////////////// |
| | | Writer::~Writer() |
| | | { |
| | | } |
| | | |
| | | |
| | | // Class FastWriter |
| | | // ////////////////////////////////////////////////////////////////// |
| | | |
| | | FastWriter::FastWriter() |
| | | : yamlCompatiblityEnabled_( false ) |
| | | { |
| | | } |
| | | |
| | | |
| | | void |
| | | FastWriter::enableYAMLCompatibility() |
| | | { |
| | | yamlCompatiblityEnabled_ = true; |
| | | } |
| | | |
| | | |
| | | std::string |
| | | FastWriter::write( const Value &root ) |
| | | { |
| | | document_ = ""; |
| | | writeValue( root ); |
| | | document_ += "\n"; |
| | | return document_; |
| | | } |
| | | |
| | | |
| | | void |
| | | FastWriter::writeValue( const Value &value ) |
| | | { |
| | | switch ( value.type() ) |
| | | { |
| | | case nullValue: |
| | | document_ += "null"; |
| | | break; |
| | | case intValue: |
| | | document_ += valueToString( value.asInt() ); |
| | | break; |
| | | case uintValue: |
| | | document_ += valueToString( value.asUInt() ); |
| | | break; |
| | | case realValue: |
| | | document_ += valueToString( value.asDouble() ); |
| | | break; |
| | | case stringValue: |
| | | document_ += valueToQuotedString( value.asCString() ); |
| | | break; |
| | | case booleanValue: |
| | | document_ += valueToString( value.asBool() ); |
| | | break; |
| | | case arrayValue: |
| | | { |
| | | document_ += "["; |
| | | int size = value.size(); |
| | | for ( int index =0; index < size; ++index ) |
| | | { |
| | | if ( index > 0 ) |
| | | document_ += ","; |
| | | writeValue( value[index] ); |
| | | } |
| | | document_ += "]"; |
| | | } |
| | | break; |
| | | case objectValue: |
| | | { |
| | | Value::Members members( value.getMemberNames() ); |
| | | document_ += "{"; |
| | | for ( Value::Members::iterator it = members.begin(); |
| | | it != members.end(); |
| | | ++it ) |
| | | { |
| | | const std::string &name = *it; |
| | | if ( it != members.begin() ) |
| | | document_ += ","; |
| | | document_ += valueToQuotedString( name.c_str() ); |
| | | document_ += yamlCompatiblityEnabled_ ? ": " |
| | | : ":"; |
| | | writeValue( value[name] ); |
| | | } |
| | | document_ += "}"; |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Class StyledWriter |
| | | // ////////////////////////////////////////////////////////////////// |
| | | |
| | | StyledWriter::StyledWriter() |
| | | : rightMargin_( 74 ) |
| | | , indentSize_( 3 ) |
| | | { |
| | | } |
| | | |
| | | |
| | | std::string |
| | | StyledWriter::write( const Value &root ) |
| | | { |
| | | document_ = ""; |
| | | addChildValues_ = false; |
| | | indentString_ = ""; |
| | | writeCommentBeforeValue( root ); |
| | | writeValue( root ); |
| | | writeCommentAfterValueOnSameLine( root ); |
| | | document_ += "\n"; |
| | | return document_; |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledWriter::writeValue( const Value &value ) |
| | | { |
| | | switch ( value.type() ) |
| | | { |
| | | case nullValue: |
| | | pushValue( "null" ); |
| | | break; |
| | | case intValue: |
| | | pushValue( valueToString( value.asInt() ) ); |
| | | break; |
| | | case uintValue: |
| | | pushValue( valueToString( value.asUInt() ) ); |
| | | break; |
| | | case realValue: |
| | | pushValue( valueToString( value.asDouble() ) ); |
| | | break; |
| | | case stringValue: |
| | | pushValue( valueToQuotedString( value.asCString() ) ); |
| | | break; |
| | | case booleanValue: |
| | | pushValue( valueToString( value.asBool() ) ); |
| | | break; |
| | | case arrayValue: |
| | | writeArrayValue( value); |
| | | break; |
| | | case objectValue: |
| | | { |
| | | Value::Members members( value.getMemberNames() ); |
| | | if ( members.empty() ) |
| | | pushValue( "{}" ); |
| | | else |
| | | { |
| | | writeWithIndent( "{" ); |
| | | indent(); |
| | | Value::Members::iterator it = members.begin(); |
| | | while ( true ) |
| | | { |
| | | const std::string &name = *it; |
| | | const Value &childValue = value[name]; |
| | | writeCommentBeforeValue( childValue ); |
| | | writeWithIndent( valueToQuotedString( name.c_str() ) ); |
| | | document_ += " : "; |
| | | writeValue( childValue ); |
| | | if ( ++it == members.end() ) |
| | | { |
| | | writeCommentAfterValueOnSameLine( childValue ); |
| | | break; |
| | | } |
| | | document_ += ","; |
| | | writeCommentAfterValueOnSameLine( childValue ); |
| | | } |
| | | unindent(); |
| | | writeWithIndent( "}" ); |
| | | } |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledWriter::writeArrayValue( const Value &value ) |
| | | { |
| | | unsigned size = value.size(); |
| | | if ( size == 0 ) |
| | | pushValue( "[]" ); |
| | | else |
| | | { |
| | | bool isArrayMultiLine = isMultineArray( value ); |
| | | if ( isArrayMultiLine ) |
| | | { |
| | | writeWithIndent( "[" ); |
| | | indent(); |
| | | bool hasChildValue = !childValues_.empty(); |
| | | unsigned index =0; |
| | | while ( true ) |
| | | { |
| | | const Value &childValue = value[index]; |
| | | writeCommentBeforeValue( childValue ); |
| | | if ( hasChildValue ) |
| | | writeWithIndent( childValues_[index] ); |
| | | else |
| | | { |
| | | writeIndent(); |
| | | writeValue( childValue ); |
| | | } |
| | | if ( ++index == size ) |
| | | { |
| | | writeCommentAfterValueOnSameLine( childValue ); |
| | | break; |
| | | } |
| | | document_ += ","; |
| | | writeCommentAfterValueOnSameLine( childValue ); |
| | | } |
| | | unindent(); |
| | | writeWithIndent( "]" ); |
| | | } |
| | | else // output on a single line |
| | | { |
| | | assert( childValues_.size() == size ); |
| | | document_ += "[ "; |
| | | for ( unsigned index =0; index < size; ++index ) |
| | | { |
| | | if ( index > 0 ) |
| | | document_ += ", "; |
| | | document_ += childValues_[index]; |
| | | } |
| | | document_ += " ]"; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | bool |
| | | StyledWriter::isMultineArray( const Value &value ) |
| | | { |
| | | int size = value.size(); |
| | | bool isMultiLine = size*3 >= rightMargin_ ; |
| | | childValues_.clear(); |
| | | for ( int index =0; index < size && !isMultiLine; ++index ) |
| | | { |
| | | const Value &childValue = value[index]; |
| | | isMultiLine = isMultiLine || |
| | | ( (childValue.isArray() || childValue.isObject()) && |
| | | childValue.size() > 0 ); |
| | | } |
| | | if ( !isMultiLine ) // check if line length > max line length |
| | | { |
| | | childValues_.reserve( size ); |
| | | addChildValues_ = true; |
| | | int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' |
| | | for ( int index =0; index < size && !isMultiLine; ++index ) |
| | | { |
| | | writeValue( value[index] ); |
| | | lineLength += int( childValues_[index].length() ); |
| | | isMultiLine = isMultiLine && hasCommentForValue( value[index] ); |
| | | } |
| | | addChildValues_ = false; |
| | | isMultiLine = isMultiLine || lineLength >= rightMargin_; |
| | | } |
| | | return isMultiLine; |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledWriter::pushValue( const std::string &value ) |
| | | { |
| | | if ( addChildValues_ ) |
| | | childValues_.push_back( value ); |
| | | else |
| | | document_ += value; |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledWriter::writeIndent() |
| | | { |
| | | if ( !document_.empty() ) |
| | | { |
| | | char last = document_[document_.length()-1]; |
| | | if ( last == ' ' ) // already indented |
| | | return; |
| | | if ( last != '\n' ) // Comments may add new-line |
| | | document_ += '\n'; |
| | | } |
| | | document_ += indentString_; |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledWriter::writeWithIndent( const std::string &value ) |
| | | { |
| | | writeIndent(); |
| | | document_ += value; |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledWriter::indent() |
| | | { |
| | | indentString_ += std::string( indentSize_, ' ' ); |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledWriter::unindent() |
| | | { |
| | | assert( int(indentString_.size()) >= indentSize_ ); |
| | | indentString_.resize( indentString_.size() - indentSize_ ); |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledWriter::writeCommentBeforeValue( const Value &root ) |
| | | { |
| | | if ( !root.hasComment( commentBefore ) ) |
| | | return; |
| | | document_ += normalizeEOL( root.getComment( commentBefore ) ); |
| | | document_ += "\n"; |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledWriter::writeCommentAfterValueOnSameLine( const Value &root ) |
| | | { |
| | | if ( root.hasComment( commentAfterOnSameLine ) ) |
| | | document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); |
| | | |
| | | if ( root.hasComment( commentAfter ) ) |
| | | { |
| | | document_ += "\n"; |
| | | document_ += normalizeEOL( root.getComment( commentAfter ) ); |
| | | document_ += "\n"; |
| | | } |
| | | } |
| | | |
| | | |
| | | bool |
| | | StyledWriter::hasCommentForValue( const Value &value ) |
| | | { |
| | | return value.hasComment( commentBefore ) |
| | | || value.hasComment( commentAfterOnSameLine ) |
| | | || value.hasComment( commentAfter ); |
| | | } |
| | | |
| | | |
| | | std::string |
| | | StyledWriter::normalizeEOL( const std::string &text ) |
| | | { |
| | | std::string normalized; |
| | | normalized.reserve( text.length() ); |
| | | const char *begin = text.c_str(); |
| | | const char *end = begin + text.length(); |
| | | const char *current = begin; |
| | | while ( current != end ) |
| | | { |
| | | char c = *current++; |
| | | if ( c == '\r' ) // mac or dos EOL |
| | | { |
| | | if ( *current == '\n' ) // convert dos EOL |
| | | ++current; |
| | | normalized += '\n'; |
| | | } |
| | | else // handle unix EOL & other char |
| | | normalized += c; |
| | | } |
| | | return normalized; |
| | | } |
| | | |
| | | |
| | | // Class StyledStreamWriter |
| | | // ////////////////////////////////////////////////////////////////// |
| | | |
| | | StyledStreamWriter::StyledStreamWriter( std::string indentation ) |
| | | : document_(NULL) |
| | | , rightMargin_( 74 ) |
| | | , indentation_( indentation ) |
| | | { |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledStreamWriter::write( std::ostream &out, const Value &root ) |
| | | { |
| | | document_ = &out; |
| | | addChildValues_ = false; |
| | | indentString_ = ""; |
| | | writeCommentBeforeValue( root ); |
| | | writeValue( root ); |
| | | writeCommentAfterValueOnSameLine( root ); |
| | | *document_ << "\n"; |
| | | document_ = NULL; // Forget the stream, for safety. |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledStreamWriter::writeValue( const Value &value ) |
| | | { |
| | | switch ( value.type() ) |
| | | { |
| | | case nullValue: |
| | | pushValue( "null" ); |
| | | break; |
| | | case intValue: |
| | | pushValue( valueToString( value.asInt() ) ); |
| | | break; |
| | | case uintValue: |
| | | pushValue( valueToString( value.asUInt() ) ); |
| | | break; |
| | | case realValue: |
| | | pushValue( valueToString( value.asDouble() ) ); |
| | | break; |
| | | case stringValue: |
| | | pushValue( valueToQuotedString( value.asCString() ) ); |
| | | break; |
| | | case booleanValue: |
| | | pushValue( valueToString( value.asBool() ) ); |
| | | break; |
| | | case arrayValue: |
| | | writeArrayValue( value); |
| | | break; |
| | | case objectValue: |
| | | { |
| | | Value::Members members( value.getMemberNames() ); |
| | | if ( members.empty() ) |
| | | pushValue( "{}" ); |
| | | else |
| | | { |
| | | writeWithIndent( "{" ); |
| | | indent(); |
| | | Value::Members::iterator it = members.begin(); |
| | | while ( true ) |
| | | { |
| | | const std::string &name = *it; |
| | | const Value &childValue = value[name]; |
| | | writeCommentBeforeValue( childValue ); |
| | | writeWithIndent( valueToQuotedString( name.c_str() ) ); |
| | | *document_ << " : "; |
| | | writeValue( childValue ); |
| | | if ( ++it == members.end() ) |
| | | { |
| | | writeCommentAfterValueOnSameLine( childValue ); |
| | | break; |
| | | } |
| | | *document_ << ","; |
| | | writeCommentAfterValueOnSameLine( childValue ); |
| | | } |
| | | unindent(); |
| | | writeWithIndent( "}" ); |
| | | } |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledStreamWriter::writeArrayValue( const Value &value ) |
| | | { |
| | | unsigned size = value.size(); |
| | | if ( size == 0 ) |
| | | pushValue( "[]" ); |
| | | else |
| | | { |
| | | bool isArrayMultiLine = isMultineArray( value ); |
| | | if ( isArrayMultiLine ) |
| | | { |
| | | writeWithIndent( "[" ); |
| | | indent(); |
| | | bool hasChildValue = !childValues_.empty(); |
| | | unsigned index =0; |
| | | while ( true ) |
| | | { |
| | | const Value &childValue = value[index]; |
| | | writeCommentBeforeValue( childValue ); |
| | | if ( hasChildValue ) |
| | | writeWithIndent( childValues_[index] ); |
| | | else |
| | | { |
| | | writeIndent(); |
| | | writeValue( childValue ); |
| | | } |
| | | if ( ++index == size ) |
| | | { |
| | | writeCommentAfterValueOnSameLine( childValue ); |
| | | break; |
| | | } |
| | | *document_ << ","; |
| | | writeCommentAfterValueOnSameLine( childValue ); |
| | | } |
| | | unindent(); |
| | | writeWithIndent( "]" ); |
| | | } |
| | | else // output on a single line |
| | | { |
| | | assert( childValues_.size() == size ); |
| | | *document_ << "[ "; |
| | | for ( unsigned index =0; index < size; ++index ) |
| | | { |
| | | if ( index > 0 ) |
| | | *document_ << ", "; |
| | | *document_ << childValues_[index]; |
| | | } |
| | | *document_ << " ]"; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | bool |
| | | StyledStreamWriter::isMultineArray( const Value &value ) |
| | | { |
| | | int size = value.size(); |
| | | bool isMultiLine = size*3 >= rightMargin_ ; |
| | | childValues_.clear(); |
| | | for ( int index =0; index < size && !isMultiLine; ++index ) |
| | | { |
| | | const Value &childValue = value[index]; |
| | | isMultiLine = isMultiLine || |
| | | ( (childValue.isArray() || childValue.isObject()) && |
| | | childValue.size() > 0 ); |
| | | } |
| | | if ( !isMultiLine ) // check if line length > max line length |
| | | { |
| | | childValues_.reserve( size ); |
| | | addChildValues_ = true; |
| | | int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' |
| | | for ( int index =0; index < size && !isMultiLine; ++index ) |
| | | { |
| | | writeValue( value[index] ); |
| | | lineLength += int( childValues_[index].length() ); |
| | | isMultiLine = isMultiLine && hasCommentForValue( value[index] ); |
| | | } |
| | | addChildValues_ = false; |
| | | isMultiLine = isMultiLine || lineLength >= rightMargin_; |
| | | } |
| | | return isMultiLine; |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledStreamWriter::pushValue( const std::string &value ) |
| | | { |
| | | if ( addChildValues_ ) |
| | | childValues_.push_back( value ); |
| | | else |
| | | *document_ << value; |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledStreamWriter::writeIndent() |
| | | { |
| | | /* |
| | | Some comments in this method would have been nice. ;-) |
| | | |
| | | if ( !document_.empty() ) |
| | | { |
| | | char last = document_[document_.length()-1]; |
| | | if ( last == ' ' ) // already indented |
| | | return; |
| | | if ( last != '\n' ) // Comments may add new-line |
| | | *document_ << '\n'; |
| | | } |
| | | */ |
| | | *document_ << '\n' << indentString_; |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledStreamWriter::writeWithIndent( const std::string &value ) |
| | | { |
| | | writeIndent(); |
| | | *document_ << value; |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledStreamWriter::indent() |
| | | { |
| | | indentString_ += indentation_; |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledStreamWriter::unindent() |
| | | { |
| | | assert( indentString_.size() >= indentation_.size() ); |
| | | indentString_.resize( indentString_.size() - indentation_.size() ); |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledStreamWriter::writeCommentBeforeValue( const Value &root ) |
| | | { |
| | | if ( !root.hasComment( commentBefore ) ) |
| | | return; |
| | | *document_ << normalizeEOL( root.getComment( commentBefore ) ); |
| | | *document_ << "\n"; |
| | | } |
| | | |
| | | |
| | | void |
| | | StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root ) |
| | | { |
| | | if ( root.hasComment( commentAfterOnSameLine ) ) |
| | | *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); |
| | | |
| | | if ( root.hasComment( commentAfter ) ) |
| | | { |
| | | *document_ << "\n"; |
| | | *document_ << normalizeEOL( root.getComment( commentAfter ) ); |
| | | *document_ << "\n"; |
| | | } |
| | | } |
| | | |
| | | |
| | | bool |
| | | StyledStreamWriter::hasCommentForValue( const Value &value ) |
| | | { |
| | | return value.hasComment( commentBefore ) |
| | | || value.hasComment( commentAfterOnSameLine ) |
| | | || value.hasComment( commentAfter ); |
| | | } |
| | | |
| | | |
| | | std::string |
| | | StyledStreamWriter::normalizeEOL( const std::string &text ) |
| | | { |
| | | std::string normalized; |
| | | normalized.reserve( text.length() ); |
| | | const char *begin = text.c_str(); |
| | | const char *end = begin + text.length(); |
| | | const char *current = begin; |
| | | while ( current != end ) |
| | | { |
| | | char c = *current++; |
| | | if ( c == '\r' ) // mac or dos EOL |
| | | { |
| | | if ( *current == '\n' ) // convert dos EOL |
| | | ++current; |
| | | normalized += '\n'; |
| | | } |
| | | else // handle unix EOL & other char |
| | | normalized += c; |
| | | } |
| | | return normalized; |
| | | } |
| | | |
| | | |
| | | std::ostream& operator<<( std::ostream &sout, const Value &root ) |
| | | { |
| | | Json::StyledStreamWriter writer; |
| | | writer.write(sout, root); |
| | | return sout; |
| | | } |
| | | |
| | | |
| | | } // namespace Json |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | Import( 'env buildLibrary' ) |
| | | |
| | | buildLibrary( env, Split( """ |
| | | json_reader.cpp |
| | | json_value.cpp |
| | | json_writer.cpp |
| | | """ ), |
| | | 'json' ) |