/*
 * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.micode.notes.gtask.data;

import android.appwidget.AppWidgetManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;

import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.exception.ActionFailureException;
import net.micode.notes.tool.GTaskStringUtils;
import net.micode.notes.tool.ResourceParser;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;


public class SqlNote {
    private static final String TAG = SqlNote.class.getSimpleName();

    private static final int INVALID_ID = -99999;

    public static final String[] PROJECTION_NOTE = new String[] {
            NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID,
            NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE,
            NoteColumns.NOTES_COUNT, NoteColumns.PARENT_ID, NoteColumns.SNIPPET, NoteColumns.TYPE,
            NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE, NoteColumns.SYNC_ID,
            NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID,
            NoteColumns.VERSION
    };
/*定义了一个名为SqlNote的类。这个类包含了一些静态常量和一个名为PROJECTION_NOTE的公共静态字符串数组，该数组包含了用于查询笔记数据的列名。

具体来说，PROJECTION_NOTE数组包含了以下列名：

ID： 笔记的唯一标识符
ALERTED_DATE： 笔记提醒的日期
BG_COLOR_ID： 笔记背景颜色的标识符
CREATED_DATE： 笔记创建的日期
HAS_ATTACHMENT： 笔记是否有附件
MODIFIED_DATE： 笔记最后修改的日期
NOTES_COUNT： 笔记下的子笔记数量
PARENT_ID： 父笔记的唯一标识符
片段： 笔记的摘要
类型： 笔记的类型
WIDGET_ID： 关联的小部件的唯一标识符
WIDGET_TYPE： 关联的小部件的类型
SYNC_ID： 与笔记相关的同步服务的唯一标识符
LOCAL_MODIFIED： 笔记是否在本地被修改过
ORIGIN_PARENT_ID： 笔记的原始父笔记的唯一标识符
GTASK_ID： 与Google Tasks相关的唯一标识符
版本： 笔记的版本号
这些列名可以用于查询笔记数据，并且在SqlNote类中被定义为静态常量，以便在整个应用程序中使用。*/
    public static final int ID_COLUMN = 0;

    public static final int ALERTED_DATE_COLUMN = 1;

    public static final int BG_COLOR_ID_COLUMN = 2;

    public static final int CREATED_DATE_COLUMN = 3;

    public static final int HAS_ATTACHMENT_COLUMN = 4;

    public static final int MODIFIED_DATE_COLUMN = 5;

    public static final int NOTES_COUNT_COLUMN = 6;

    public static final int PARENT_ID_COLUMN = 7;

    public static final int SNIPPET_COLUMN = 8;

    public static final int TYPE_COLUMN = 9;

    public static final int WIDGET_ID_COLUMN = 10;

    public static final int WIDGET_TYPE_COLUMN = 11;

    public static final int SYNC_ID_COLUMN = 12;

    public static final int LOCAL_MODIFIED_COLUMN = 13;

    public static final int ORIGIN_PARENT_ID_COLUMN = 14;

    public static final int GTASK_ID_COLUMN = 15;

    public static final int VERSION_COLUMN = 16;
/*这是SqlNote类中定义的一些静态常量，它们表示在PROJECTION_NOTE数组中每个列名的索引位置。
这些常量被用于访问查询结果中特定列的值。
 */
    private Context mContext;

    private ContentResolver mContentResolver;

    private boolean mIsCreate;

    private long mId;

    private long mAlertDate;

    private int mBgColorId;

    private long mCreatedDate;

    private int mHasAttachment;

    private long mModifiedDate;

    private long mParentId;

    private String mSnippet;

    private int mType;

    private int mWidgetId;

    private int mWidgetType;

    private long mOriginParent;

    private long mVersion;

    private ContentValues mDiffNoteValues;

    private ArrayList<SqlData> mDataList;
/*这是SqlNote类中的一些成员变量，它们被用于存储笔记的数据。下面是每个变量的含义：
mContext： 上下文对象，用于访问应用程序的资源和服务。
mContentResolver： 内容解析器对象，用于访问应用程序的数据。
mIsCreate： 表示这个对象是否代表一个新创建的笔记。
mId： 笔记的唯一标识符。
mAlertDate： 笔记提醒的日期。
mBgColorId： 笔记背景颜色的标识符。
mCreatedDate： 笔记创建的日期。
mHasAttachment： 表示笔记是否有附件。
mModifiedDate： 笔记最后修改的
mParentId: 父笔记的唯一标识符。
mSnippet: 笔记的摘要。
mType: 笔记的类型。
mWidgetId: 关联的小部件的唯一标识符。
mWidgetType: 关联的小部件的类型。
mOriginParent: 笔记的原始父笔记的唯一标识符。
mVersion: 笔记的版本号。
mDiffNoteValues: 一个ContentValues对象，包含了与这个笔记相关的差异数据。
mDataList: 一个ArrayList对象，包含了这个笔记的子笔记数据。
这些成员变量被用于存储SqlNote对象的状态，并且可以用于从数据库中读取和写入笔记数据。
其中一些成员变量还可以用于在应用程序中显示笔记的相关信息。
 */
    public SqlNote(Context context) {
        mContext = context;
        mContentResolver = context.getContentResolver();
        mIsCreate = true;
        mId = INVALID_ID;
        mAlertDate = 0;
        mBgColorId = ResourceParser.getDefaultBgId(context);
        mCreatedDate = System.currentTimeMillis();
        mHasAttachment = 0;
        mModifiedDate = System.currentTimeMillis();
        mParentId = 0;
        mSnippet = "";
        mType = Notes.TYPE_NOTE;
        mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
        mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
        mOriginParent = 0;
        mVersion = 0;
        mDiffNoteValues = new ContentValues();
        mDataList = new ArrayList<SqlData>();
    }
/*这是SqlNote类的构造函数，它用于创建一个新的SqlNote对象。在构造函数中，SqlNote对象的所有成员变量都被初始化为它们的默认值。具体来说，它们的初始值如下：
mContext： 传入的上下文对象。
mContentResolver： 从上下文对象中获取的内容解析器对象。
mIsCreate： 设置为true，表示这是一个新创建
mId： 设置为INVALID_ID，表示这个笔记还没有在数据库中分配一个有效的唯一标识符。
mAlertDate： 设置为0，表示这个笔记没有提醒日期。
mBgColorId： 设置为应用程序的默认背景颜色。
mCreatedDate： 设置为当前时间的毫秒数，表示这个笔记的创建日期。
mHasAttachment： 设置为0，表示这个笔记没有附件。
mModifiedDate： 设置为当前时间的毫秒数，表示这个笔记的最后修改日期。
mParentId： 设置为0，表示这个笔记没有父笔记。
mSnippet： 设置为空字符串，表示这个笔记没有摘要。
mType： 设置为Notes.TYPE_NOTE，表示这个笔记是一个普通笔记。
mWidgetId： 设置为AppWidgetManager.INVALID_APPWIDGET_ID，表示这个笔记没有关联的小部件。
mWidgetType： 设置为Notes.TYPE_WIDGET_INVALIDE，表示这个笔记没有关联的小部件类型。
mOriginParent： 设置为0，表示这个笔记没有原始父笔记。
mVersion： 设置为0，表示这个笔记的版本号为0。
mDiffNoteValues： 创建一个新的ContentValues对象，用于存储这个笔记的差异数据。
mDataList： 创建一个新的ArrayList对象，用于存储这个笔记的子笔记数据。
在构造函数中，所有的成员变量都被初始化为它们的默认值，因此这个SqlNote对象代表了一个刚刚创建的新笔记。*/
    public SqlNote(Context context, Cursor c) {
        mContext = context;
        mContentResolver = context.getContentResolver();
        mIsCreate = false;
        loadFromCursor(c);
        mDataList = new ArrayList<SqlData>();
        if (mType == Notes.TYPE_NOTE)
            loadDataContent();
        mDiffNoteValues = new ContentValues();
    }
/*这是SqlNote类的另一个构造函数，用于从数据库游标中加载已有的SqlNote对象。在构造函数中，SqlNote对象的所有成员变量都被初始化为它们从数据库中读取的值。具体来说：
mContext： 传入的
mContentResolver： 从上下文对象中获取的内容解析器
mIsCreate： 设置为false，表示这个SqlNote对象代表的是一个已经存在于数据库中的笔记。
loadFromCursor（c）： 调用loadFromCursor（）方法，从游标c中加载所有的笔记数据，包括笔记的标题、内容、创建日期、修改日期等等。
mDataList： 创建一个新的ArrayList对象，用于存储
loadDataContent（）： 如果这个笔记是一个普通笔记，则调用loadDataContent（）方法，从数据库中加载笔记的内容。
mDiffNoteValues： 创建一个新的ContentValues对象，用于存储这个笔记的差异数据。
通过这个构造函数，SqlNote对象可以从数据库中读取已有的笔记数据，并在内存中表示这个笔记。*/
    public SqlNote(Context context, long id) {
        mContext = context;
        mContentResolver = context.getContentResolver();
        mIsCreate = false;
        loadFromCursor(id);
        mDataList = new ArrayList<SqlData>();
        if (mType == Notes.TYPE_NOTE)
            loadDataContent();
        mDiffNoteValues = new ContentValues();

    }
/*这是SqlNote类的另一个构造函数，用于从数据库中加载指定id的SqlNote对象。在构造函数中，SqlNote对象的所有成员变量都被初始化为它们从数据库中读取的值。具体来说：
mContext： 传入的上下文对象。
mContentResolver： 从上下文对象中获取的内容解析器对象。
mIsCreate： 设置为false，表示这个SqlNote对象代表的是一个已经存在于
loadFromCursor（id）： 调用loadFromCursor（）方法，从数据库中加载指定id的笔记数据，包括笔记的标题、内容、创建日期、修改日期等等。
mDataList： 创建一个新的ArrayList对象，用于存储这个笔记的子笔记数据。
loadDataContent（）： 如果这个笔记是一个普通笔记，则调用loadDataContent（）方法，从数据库中加载笔记的内容。
mDiffNoteValues： 创建一个新的ContentValues对象，用于存储这个笔记的差异数据。
通过这个构造函数，SqlNote对象可以从数据库中读取指定id的笔记数据，并在内存中表示这个笔记。*/
    private void loadFromCursor(long id) {
        Cursor c = null;
        try {
            c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)",
                    new String[] {
                        String.valueOf(id)
                    }, null);
            if (c != null) {
                c.moveToNext();
                loadFromCursor(c);
            } else {
                Log.w(TAG, "loadFromCursor: cursor = null");
            }
        } finally {
            if (c != null)
                c.close();
        }
    }
/*这是SqlNote类的一个私有方法，用于从数据库中加载指定id的笔记数据。具体来说：
首先，通过查询内容提供器Notes.CONTENT_NOTE_URI获取指定ID
然后，如果游标不为null，将游标移到第一条记录，并调用loadFromCursor(Cursor c)方法，将游标中的所有记录加载到SqlNote对象中。
最后，关闭游标对象。
通过这个方法，SqlNote对象可以从数据库中查询指定id的笔记数据，并在内存中表示这个笔记。需要注意的是，在加载数据之后一定要及时关闭游标对象，以释放资源。*/
    private void loadFromCursor(Cursor c) {
        mId = c.getLong(ID_COLUMN);
        mAlertDate = c.getLong(ALERTED_DATE_COLUMN);
        mBgColorId = c.getInt(BG_COLOR_ID_COLUMN);
        mCreatedDate = c.getLong(CREATED_DATE_COLUMN);
        mHasAttachment = c.getInt(HAS_ATTACHMENT_COLUMN);
        mModifiedDate = c.getLong(MODIFIED_DATE_COLUMN);
        mParentId = c.getLong(PARENT_ID_COLUMN);
        mSnippet = c.getString(SNIPPET_COLUMN);
        mType = c.getInt(TYPE_COLUMN);
        mWidgetId = c.getInt(WIDGET_ID_COLUMN);
        mWidgetType = c.getInt(WIDGET_TYPE_COLUMN);
        mVersion = c.getLong(VERSION_COLUMN);
    }
/*这是SqlNote类的一个私有方法，用于从游标中加载笔记数据。具体来说：
通过游标对象c获取每个属性的值，并将其赋值给SqlNote对象的对应成员变量。
其中，这些属性的含义如下：
mId： 笔记的唯一标识符。
mAlertDate： 笔记提醒时间，以毫秒为单位。
mBgColorId： 笔记背景颜色的标识符。
mCreatedDate： 笔记创建时间，以毫秒为单位。
mHasAttachment： 笔记是否有附件（0表示没有，1表示有）。
mModifiedDate： 笔记最近修改时间，以毫秒为单位。
mParentId： 笔记的父笔记的唯一标识符。
mSnippet： 笔记的摘要。
mType： 笔记的类型，可以是普通笔记、清单笔记、便签等。
mWidgetId： 笔记所关联的小部件的唯一标识符。
mWidgetType： 笔记所关联的小部件的类型。
m版本： 笔记的
通过这个方法，SqlNote对象可以从游标中加载所有的笔记数据，并在内存中表示这个笔记。*/
    private void loadDataContent() {
        Cursor c = null;
        mDataList.clear();
        try {
            c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA,
                    "(note_id=?)", new String[] {
                        String.valueOf(mId)
                    }, null);
            if (c != null) {
                if (c.getCount() == 0) {
                    Log.w(TAG, "it seems that the note has not data");
                    return;
                }
                while (c.moveToNext()) {
                    SqlData data = new SqlData(mContext, c);
                    mDataList.add(data);
                }
            } else {
                Log.w(TAG, "loadDataContent: cursor = null");
            }
        } finally {
            if (c != null)
                c.close();
        }
    }
/*这是SqlNote类的一个私有方法，用于从数据库中加载笔记的数据内容。具体来说：
首先，通过查询内容提供器Notes.CONTENT_DATA_URI获取指定笔记ID的所有
然后，清空数据列表mDataList。
接着，如果游标不为null，遍历游标中的所有记录，并使用SqlData对象将每条记录转换成一个数据对象。将这些数据对象添加到数据列表mDataList中。
最后，关闭游标对象。
通过这个方法，SqlNote对象可以从数据库中查询指定笔记id的所有数据，将其转换成数据对象，并存储在内存中。需要注意的是，在加载数据之后一定要及时关闭游标对象，以释放资源。*/
    public boolean setContent(JSONObject js) {
        try {
            JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
            if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {
                Log.w(TAG, "cannot set system folder");
            }
            /*这是SqlNote类的一个公有方法，用于设置笔记的内容。具体来说：
              首先，从Json对象js中获取笔记的元数据信息，即note对象。
              然后，判断获取的笔记类型是否为系统类型（Notes.TYPE_SYSTEM），如果是则输出警告信息并返回false。
              如果不是系统类型，则继续执行以下步骤。
              在这个方法中，对于非法的系统类型的笔记，将输出警告信息并返回false，不会更改SqlNote对象的内容。对于合法的笔记，该方法将根据传入的Json对象设置笔记的内容。但是，这个方法的具体实现并没有展示出来，需要查看该方法的后续代码才能了解更多信息。*/
            else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) {
                // for folder we can only update the snnipet and type
                String snippet = note.has(NoteColumns.SNIPPET) ? note
                        .getString(NoteColumns.SNIPPET) : "";
                if (mIsCreate || !mSnippet.equals(snippet)) {
                    mDiffNoteValues.put(NoteColumns.SNIPPET, snippet);
                }
                mSnippet = snippet;

                int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE)
                        : Notes.TYPE_NOTE;
                if (mIsCreate || mType != type) {
                    mDiffNoteValues.put(NoteColumns.TYPE, type);
                }
                mType = type;
            }
            /*如果获取的笔记类型是文件夹类型（Notes.TYPE_FOLDER），则该方法只能更新文件夹的片段（NoteColumns.SNIPPET）和类型（NoteColumns.TYPE）属性。
              如果传入的Json对象中包含片段属性（NoteColumns.SNIPPET），则将其值赋给片段变量，否则将其设置为空字符串。
              如果 SqlNote
              将当前的片段值（mSnippet）设置为获取的片段值（snippet）。
              如果传入的Json对象中包含类型属性（NoteColumns.TYPE），则将其值赋给type变量，否则将其设置为Notes.TYPE_NOTE（默认值）。
              如果 SqlNote
              将当前的类型值（mType）设置为获取的类型值（type）。
              通过这个方法，SqlNote对象可以根据传入的Json对象更新笔记的片段和类型属性。需要注意的是，对于文件夹类型的笔记，只能更新片段和类型属性，其他属性不能更改。*/
            else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) {
                JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
                long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID;
                if (mIsCreate || mId != id) {
                    mDiffNoteValues.put(NoteColumns.ID, id);
                }
                mId = id;
                /*代码检索到JSONArray叫dataArray从命名的 JSON 对象js.
                  然后，代码检查是否
                  如果mIsCreate为 true 或当前注释 ID 为mId不等于检索到的 ID，则代码将 ID 放在HashMap叫mDiffNoteValues使用put()方法与NoteColumns.ID常量作为键和检索到的 ID作为值。*/
                long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note
                        .getLong(NoteColumns.ALERTED_DATE) : 0;
                if (mIsCreate || mAlertDate != alertDate) {
                    mDiffNoteValues.put(NoteColumns.ALERTED_DATE, alertDate);
                }
                mAlertDate = alertDate;
                /*通过调用has()方法上的方法NoteColumns.ALERTED_DATE不断。如果注释有警报日期，则警报日期通过调用getLong()方法上NoteColumns.ALERTED_DATE不断。否则，它将警报日期设置为 0。
                  如果mIsCreate为 true 或当前警报日期mAlertDate不等于检索到的警报日期，则代码将警报日期放在HashMap叫mDiffNoteValues使用put()方法与NoteColumns.ALERTED_DATE常量作为键，检索到的警报日期作为值。
                  最后，代码将检索到的警报日期分配给名为mAlertDate.仅从此代码块中不清楚此变量的用途，但它可以在程序的其他地方使用。*/
                int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note
                        .getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext);
                if (mIsCreate || mBgColorId != bgColorId) {
                    mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId);
                }
                mBgColorId = bgColorId;
                /*代码检查
                如果mIsCreate为 true 或当前背景颜色 ID 为 truemBgColorId不等于检索到的背景颜色 ID，则代码将背景颜色 ID 放在HashMap叫mDiffNoteValues使用put()方法与NoteColumns.BG_COLOR_ID常量作为键和检索到的背景色标识为
                最后，代码将检索到的背景颜色 ID 分配给名为mBgColorId.仅从此代码块中不清楚此变量的用途，但它可以在程序的其他地方使用。*/
                long createDate = note.has(NoteColumns.CREATED_DATE) ? note
                        .getLong(NoteColumns.CREATED_DATE) : System.currentTimeMillis();
                if (mIsCreate || mCreatedDate != createDate) {
                    mDiffNoteValues.put(NoteColumns.CREATED_DATE, createDate);
                }
                mCreatedDate = createDate;
                /*调用has()方法上的方法NoteColumns.CREATED_DATE不断。如果注释具有创建日期，代码通过调用getLong()方法上NoteColumns.CREATED_DATE不断。否则，它将创建日期设置为当前系统时间以毫秒为单位，通过调用System.currentTimeMillis()方法。
                如果是mIsCreatemCreatedDate不等于检索到的创建日期，则代码将创建日期放在HashMap叫mDiffNoteValues使用put()方法与NoteColumns.CREATED_DATE常量作为键，检索到的创建日期作为值。
                最后，代码将检索到的创建日期分配给名为mCreatedDate.*/
                int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note
                        .getInt(NoteColumns.HAS_ATTACHMENT) : 0;
                if (mIsCreate || mHasAttachment != hasAttachment) {
                    mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment);
                }
                mHasAttachment = hasAttachment;
                /*检查注释是否具有附件。如果注释具有附件，则代码通过对常量调用方法来检索值。否则，它将值设置为 0。
                如果为 true，或者如果 的当前值不等于检索到的值，则代码使用以常量为键，检索到的值作为值的方法将值放入调用中。*/
                long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note
                        .getLong(NoteColumns.MODIFIED_DATE) : System.currentTimeMillis();
                if (mIsCreate || mModifiedDate != modifiedDate) {
                    mDiffNoteValues.put(NoteColumns.MODIFIED_DATE, modifiedDate);
                }
                mModifiedDate = modifiedDate;

                long parentId = note.has(NoteColumns.PARENT_ID) ? note
                        .getLong(NoteColumns.PARENT_ID) : 0;
                if (mIsCreate || mParentId != parentId) {
                    mDiffNoteValues.put(NoteColumns.PARENT_ID, parentId);
                }
                mParentId = parentId;

                String snippet = note.has(NoteColumns.SNIPPET) ? note
                        .getString(NoteColumns.SNIPPET) : "";
                if (mIsCreate || !mSnippet.equals(snippet)) {
                    mDiffNoteValues.put(NoteColumns.SNIPPET, snippet);
                }
                mSnippet = snippet;

                int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE)
                        : Notes.TYPE_NOTE;
                if (mIsCreate || mType != type) {
                    mDiffNoteValues.put(NoteColumns.TYPE, type);
                }
                mType = type;

                int widgetId = note.has(NoteColumns.WIDGET_ID) ? note.getInt(NoteColumns.WIDGET_ID)
                        : AppWidgetManager.INVALID_APPWIDGET_ID;
                if (mIsCreate || mWidgetId != widgetId) {
                    mDiffNoteValues.put(NoteColumns.WIDGET_ID, widgetId);
                }
                mWidgetId = widgetId;

                int widgetType = note.has(NoteColumns.WIDGET_TYPE) ? note
                        .getInt(NoteColumns.WIDGET_TYPE) : Notes.TYPE_WIDGET_INVALIDE;
                if (mIsCreate || mWidgetType != widgetType) {
                    mDiffNoteValues.put(NoteColumns.WIDGET_TYPE, widgetType);
                }
                mWidgetType = widgetType;
                /*这段代码在一个笔记应用中用于更新笔记的不同属性。下面是每个段的注释：
                首先检查笔记对象中是否包含最后修改日期属性，如果是则获取该属性的值，否则将当前时间作为最后修改日期。如果笔记对象是新创建的或最后修改日期与之前不同，则将其放入待更新的笔记属性集合中。最后将最后修改日期赋值给实例变量mModifiedDate。
                然后检查笔记对象中是否包含父笔记ID属性，如果是则获取该属性的值，否则将0作为父笔记ID。如果笔记对象是新创建的或父笔记ID与之前不同，则将其放入待更新的笔记属性集合中。最后将父笔记ID赋值给实例变量mParentId。
                接下来检查笔记对象中是否包含摘要属性，如果是则获取该属性的值，否则将空字符串作为摘要。如果笔记对象是新创建的或摘要与之前不同，则将其放入待更新的笔记属性集合中。最后将摘要赋值给实例变量mSnippet。
                然后检查笔记对象中是否包含类型属性，如果是则获取该属性的值，否则将默认类型Note作为类型。如果笔记对象是新创建的或类型与之前不同，则将其放入待更新的笔记属性集合中。最后将类型赋值给实例变量mType。
                接着检查笔记对象中是否包含小部件ID属性，如果是则获取该属性的值，否则将无效的小部件ID作为小部件ID。如果笔记对象是新创建的或小部件ID与之前不同，则将其放入待更新的笔记属性集合中。最后将小部件ID赋值给实例变量mWidgetId。
                最后检查笔记对象中是否包含小部件类型属性，如果是则获取该属性的值，否则将无效的小部件类型作为小部件类型。如果笔记对象是新创建的或小部件类型与之前不同，则将其放入待更新的笔记属性集合中。最后将小部件类型赋值给实例变量mWidgetType。*/
                long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note
                        .getLong(NoteColumns.ORIGIN_PARENT_ID) : 0;
                if (mIsCreate || mOriginParent != originParent) {
                    mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent);
                }
                mOriginParent = originParent;
                /*首先检查笔记对象中是否包含原始父笔记ID属性，如果是则获取该属性的值，否则将0作为原始父笔记ID
                如果笔记对象是新创建的或原始父笔记ID与之前不同，则将其放入待更新的笔记属性集合中。
                最后将原始父笔记ID赋值给实例变量mOriginParent。*/
                for (int i = 0; i < dataArray.length(); i++) {
                    JSONObject data = dataArray.getJSONObject(i);
                    SqlData sqlData = null;
                    if (data.has(DataColumns.ID)) {
                        long dataId = data.getLong(DataColumns.ID);
                        for (SqlData temp : mDataList) {
                            if (dataId == temp.getId()) {
                                sqlData = temp;
                            }
                        }
                    }
                    /*首先，使用for循环遍历数据数组中的每个JSON对象。
                    然后，检查JSON对象是否具有ID属性。如果有，将其与数据列表中的每个SqlData对象进行比较，以查找具有相同ID的SqlData对象。如果找到了匹配的SqlData对象，则将其赋值给sqlData变量。
                    如果未找到匹配项，则sqlData变量将保持为null。*/
                    if (sqlData == null) {
                        sqlData = new SqlData(mContext);
                        mDataList.add(sqlData);
                    }
                   /*如果在数据列表中未找到具有与JSON对象相同ID的SqlData对象，则创建一个新的SqlData对象，并将其添加到数据列表中。
                   如果找到了匹配的SqlData对象，则sqlData变量已经被赋值为该对象，不需要进行其他处理。*/
                    sqlData.setContent(data);
                }
            }
        } catch (JSONException e) {
            Log.e(TAG, e.toString());
            e.printStackTrace();
            return false;
        }
        return true;
    }
/*如果找到了具有与JSON对象相同ID的SqlData对象，则调用该对象的setContent方法，并将JSON对象作为参数传递给该方法。该方法将使用JSON对象更新SqlData对象的属性值。如果未找到匹配的SqlData对象，则不需要进行任何处理。
最后，try-catch语句用于捕获可能的JSONException异常，并在发生异常时打印错误信息。
如果没有发生异常，则返回值为true。*/
    public JSONObject getContent() {
        try {
            JSONObject js = new JSONObject();

            if (mIsCreate) {
                Log.e(TAG, "it seems that we haven't created this in database yet");
                return null;
            }
/*首先创建一个新的JSONObject对象。然后检查mIsCreate属性的值。
如果为true，则表示该SqlData对象尚未被创建到数据库中，此时将打印一个错误信息，并返回null。
否则，将使用put方法将SqlData对象的属性值逐个添加到JSONObject对象中。
最后，返回包含SqlData对象属性值的JSONObject对象。*/
            JSONObject note = new JSONObject();
            if (mType == Notes.TYPE_NOTE) {
                note.put(NoteColumns.ID, mId);
                note.put(NoteColumns.ALERTED_DATE, mAlertDate);
                note.put(NoteColumns.BG_COLOR_ID, mBgColorId);
                note.put(NoteColumns.CREATED_DATE, mCreatedDate);
                note.put(NoteColumns.HAS_ATTACHMENT, mHasAttachment);
                note.put(NoteColumns.MODIFIED_DATE, mModifiedDate);
                note.put(NoteColumns.PARENT_ID, mParentId);
                note.put(NoteColumns.SNIPPET, mSnippet);
                note.put(NoteColumns.TYPE, mType);
                note.put(NoteColumns.WIDGET_ID, mWidgetId);
                note.put(NoteColumns.WIDGET_TYPE, mWidgetType);
                note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent);
                js.put(GTaskStringUtils.META_HEAD_NOTE, note);

                JSONArray dataArray = new JSONArray();
                for (SqlData sqlData : mDataList) {
                    JSONObject data = sqlData.getContent();
                    if (data != null) {
                        dataArray.put(data);
                    }
                }
                js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);
            }
            /*首先创建一个新的JSONObject对象note，并使用put方法将SqlData对象的属性值逐个添加到该对象中。然后，将note对象添加到新创建的JSONObject对象js中，并使用put方法将其作为GTaskStringUtils.META_HEAD_NOTE键的值。
            接下来，创建一个新的JSONArray对象dataArray，并使用for循环遍历mDataList列表中的每个SqlData对象。对于每个SqlData对象，调用其getContent方法获取一个JSON对象，并检查该对象是否为null。如果不为null，则将其添加到dataArray数组中。
            最后，将dataArray数组添加到js对象中，并使用put方法将其作为GTaskStringUtils.META_HEAD_DATA键的值。如果mType属性不等于Notes.TYPE_NOTE，则不会将任何数据添加到js对象中。*/
            else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) {
                note.put(NoteColumns.ID, mId);
                note.put(NoteColumns.TYPE, mType);
                note.put(NoteColumns.SNIPPET, mSnippet);
                js.put(GTaskStringUtils.META_HEAD_NOTE, note);
            }

            return js;
        } catch (JSONException e) {
            Log.e(TAG, e.toString());
            e.printStackTrace();
        }
        return null;
    }
        /*如果mType属性等于Notes.TYPE_FOLDER或Notes.TYPE_SYSTEM，则创建一个新的JSONObject对象note，并使用put方法将SqlData对象的属性值逐个添加到该对象中。然后，将note对象添加到新创建的JSONObject对象js中，并使用put方法将其作为GTaskStringUtils.META_HEAD_NOTE键的值。
        如果发生JSONException异常，则在控制台中打印错误信息，并返回null。如果没有发生异常，则返回包含SqlData对象属性值的JSONObject对象。js
        总的来说，这段代码的作用是将SqlData对象的属性值转换为JSON格式，并将其添加到新创建的JSONObject对象中。*/
    public void setParentId(long id) {
        mParentId = id;
        mDiffNoteValues.put(NoteColumns.PARENT_ID, id);
    }

    public void setGtaskId(String gid) {
        mDiffNoteValues.put(NoteColumns.GTASK_ID, gid);
    }

    public void setSyncId(long syncId) {
        mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId);
    }

    public void resetLocalModified() {
        mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0);
    }

    public long getId() {
        return mId;
    }

    public long getParentId() {
        return mParentId;
    }

    public String getSnippet() {
        return mSnippet;
    }

    public boolean isNoteType() {
        return mType == Notes.TYPE_NOTE;
    }
    /*setParentId(long id): 将id参数值赋给mParentId属性，并使用put方法将NoteColumns.PARENT_ID和id添加到mDiffNoteValues对象中。
    setGtaskId(String gid): 使用put方法将NoteColumns.GTASK_ID和gid添加到mDiffNoteValues对象中。
    setSyncId(long syncId): 使用put方法将NoteColumns.SYNC_ID和syncId添加到mDiffNoteValues对象中。
    resetLocalModified(): 使用put方法将和0添加到NoteColumns.LOCAL_MODIFIEDmDiffNoteValues对象中。该方法用于重置SqlData对象的本地修改标志。
    getId(): 返回mId属性的值。
    getParentId(): 返回mParentId属性的值。
    getSnippet(): 返回mSnippet属性的值。
    isNoteType(): 如果mType属性等于，则返回Notes.TYPE_NOTEtrue，否则返回false。该方法用于判断该SqlData对象是否是一个笔记类型的对象。*/
    public void commit(boolean validateVersion) {
        if (mIsCreate) {
            if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) {
                mDiffNoteValues.remove(NoteColumns.ID);
            }

            Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues);
            try {
                mId = Long.valueOf(uri.getPathSegments().get(1));
            } catch (NumberFormatException e) {
                Log.e(TAG, "Get note id error :" + e.toString());
                throw new ActionFailureException("create note failed");
            }
            if (mId == 0) {
                throw new IllegalStateException("Create thread id failed");
            }

            if (mType == Notes.TYPE_NOTE) {
                for (SqlData sqlData : mDataList) {
                    sqlData.commit(mId, false, -1);
                }
            }
        }
        /*如果mIsCreate属性为true，则表示该SqlData对象是新创建的，需要将其写入数据库。如果mId属性的值等于INVALID_ID（即-1）并且mDiffNoteValues对象中包含NoteColumns.ID键，那么将该键从mDiffNoteValues对象中移除。然后，使用insert方法将mDiffNoteValues对象中的属性值添加到Notes.CONTENT_NOTE_URI中，并将返回的Uri对象赋给uri变量。
        接下来，使用getPathSegments方法获取uri对象中的路径段，并将第二个路径段（即路径中的第二个元素）转换为long类型，并将其赋给mId属性。如果在转换过程中发生NumberFormatException异常，则在控制台中打印错误信息并抛出ActionFailureException异常。如果mId属性的值等于0，则抛出IllegalStateException异常。
        如果属性等于，则表示该mTypeNotes.TYPE_NOTESqlData对象是一个笔记类型的对象，并且它包含一个mDataList列表，该列表中包含其他SqlData对象。在这种情况下，需要递归调用每个SqlData对象的commit方法，并将mId的值作为参数传递给该方法，以便将这些子对象插入到数据库中。*/
        else {
            if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) {
                Log.e(TAG, "No such note");
                throw new IllegalStateException("Try to update note with invalid id");
            }
            if (mDiffNoteValues.size() > 0) {
                mVersion ++;
                int result = 0;
                if (!validateVersion) {
                    result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
                            + NoteColumns.ID + "=?)", new String[] {
                        String.valueOf(mId)
                    });
                } else {
                    result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
                            + NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)",
                            new String[] {
                                    String.valueOf(mId), String.valueOf(mVersion)
                            });
                }
                if (result == 0) {
                    Log.w(TAG, "there is no update. maybe user updates note when syncing");
                }
            }

            if (mType == Notes.TYPE_NOTE) {
                for (SqlData sqlData : mDataList) {
                    sqlData.commit(mId, validateVersion, mVersion);
                }
            }
        }

        // refresh local info
        loadFromCursor(mId);
        if (mType == Notes.TYPE_NOTE)
            loadDataContent();

        mDiffNoteValues.clear();
        mIsCreate = false;
    }
}
/*如果mIsCreate属性为false，则表示该SqlData对象是已存在于数据库中的对象，需要更新其属性值。首先，需要检查mId属性的值是否为有效值。如果mId属性的值小于等于0且不等于Notes.ID_ROOT_FOLDER和Notes.ID_CALL_RECORD_FOLDER，则表示该笔记不存在，会打印错误信息并抛出IllegalStateException异常。然后，检查mDiffNoteValues对象是否包含要更新的属性值，如果包含，则需要更新笔记的版本号mVersion，并使用update方法将更改后的属性值写入数据库中。
如果参数为validateVersiontrue，则表示需要验证版本号。在这种情况下，会将笔记的版本号mVersion作为参数传递给update方法，并在WHERE子句中添加一个条件，以确保要更新的笔记版本号小于等于当前版本号。如果validateVersion参数为false，则不需要验证版本号。
如果更新操作成功，则方法会返回受影响的行数。如果返回值为0，则表示更新操作没有修改任何行，会在控制台中打印警告信息。update
如果mType属性等于Notes.TYPE_NOTE，则表示该SqlData对象是一个笔记类型的对象，并且它包含一个mDataList列表，该列表中包含其他SqlData对象。在这种情况下，需要递归调用每个SqlData对象的commit方法，并将mId和validateVersion的值作为参数传递给该方法。
最后，刷新本地信息，清空mDiffNoteValues对象中的属性值，将mIsCreate属性设置为false，并完成方法的执行。*/