diff --git a/doc/蔡玉祥注释的代码/MetaData.java b/doc/蔡玉祥注释的代码/MetaData.java new file mode 100644 index 0000000..b08b2f8 --- /dev/null +++ b/doc/蔡玉祥注释的代码/MetaData.java @@ -0,0 +1,122 @@ +/* + * 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.database.Cursor; +import android.util.Log; + +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONException; +import org.json.JSONObject; + + +public class MetaData extends Task { + private final static String TAG = MetaData.class.getSimpleName(); + + private String mRelatedGid = null; + + public void setMeta(String gid, JSONObject metaInfo) { + try { + metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); + } catch (JSONException e) { + Log.e(TAG, "failed to put related gid"); + } + setNotes(metaInfo.toString()); + setName(GTaskStringUtils.META_NOTE_NAME); + } + /* + 这段代码定义了一个名为 MetaData 的类,它继承了 Task 类。 + Meta Data 类有一个私有的属性 mRelatedGid,它的值为 null。 + 类中有两个方法,分别为 setMeta 和 getRelatedGid。 + setMeta 方法接受两个参数,一个字符串 gid 和一个 JSON 对象 metaInfo。 + 此方法会尝试在 metaInfo 对象中添加一个以 GTaskStringUtils.META_HEAD_GTASK_ID 为键、gid 为值的键值对,如果添加失败则输出一个错误日志。 + 随后该方法使用 setNotes 方法将 metaInfo 对象的字符串形式设置为任务的说明,并设置任务的名称为 GTaskStringUtils.META_NOTE_NAME。 + getRelatedGid 方法返回 mRelatedGid 属性的值。 + */ + + public String getRelatedGid() { + return mRelatedGid; + } + /*getRelatedGid() 方法: + 该方法返回 MetaData 对象的 mRelatedGid 字段,即任务的全局唯一标识符 gid。 + */ + + @Override + public boolean isWorthSaving() { + return getNotes() != null; + } + /*isWorthSaving() 方法:该方法覆盖了 Task 类中的同名方法。 + 该方法返回一个布尔值,指示任务是否值得保存。 + 在这个实现中,如果任务的备注信息不为 null,则任务值得保存,返回 true; + 否则返回 false。 + */ + + + @Override + public void setContentByRemoteJSON(JSONObject js) {// 调用父类的方法来设置 JSON 内容 + super.setContentByRemoteJSON(js);// 检查是否存在与对象相关联的注释(notes) + if (getNotes() != null) { + try { + // 将注释内容解析为 JSON 对象 + JSONObject metaInfo = new JSONObject(getNotes().trim()); + // 从 JSON 对象中提取 GTASK_ID 字段的值,并将其存储在 mRelatedGid 变量中 + mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); + } catch (JSONException e) { + ///如果在解析注释时出现异常,则将 mRelatedGid 变量设置为 null,并记录一个警告日志 + Log.w(TAG, "failed to get related gid"); + mRelatedGid = null; + } + } + } + /*这段代码是 MetaData 类的另外两个方法,isWorthSaving 和 setContentByRemoteJSON。 + + isWorthSaving 方法覆盖了 Task 类中的同名方法。 + 它返回一个布尔值,如果任务的说明不为 null,则返回 true,否则返回 false。 + + setContentByRemoteJSON 方法覆盖了 Task 类中的同名方法。 + 它接受一个 JSONObject 对象 js。首先,它调用了 Task 类中的 setContentByRemoteJSON 方法。 + 然后,它检查任务的说明是否为 null。 + 如果不是 null,则将它转换为一个 JSONObject 对象 metaInfo,然后从 metaInfo 对象中获取以 GTaskStringUtils.META_HEAD_GTASK_ID 为键的字符串值, + 将它赋值给 mRelatedGid 属性。如果无法从 metaInfo 对象中获取相关键值对,则将 mRelatedGid 属性赋值为 null,并输出一个警告日志。*/ + + @Override + public void setContentByLocalJSON(JSONObject js) { + // this function should not be called + throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); + } + + @Override + public JSONObject getLocalJSONFromContent() { + throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); + } + + @Override + public int getSyncAction(Cursor c) { + throw new IllegalAccessError("MetaData:getSyncAction should not be called"); + } + +} +/*这段代码定义了三个方法,均为覆盖(override)自父类 Task 的方法。这些方法被 MetaData 类重写,主要是为了禁止使用它们来保证程序的正确性。 + +setContentByLocalJSON 方法不应该被调用,因此该方法的实现中直接抛出了一个 IllegalAccessError 异常,提示该方法不应该被调用。 + +getLocalJSONFromContent 方法也不应该被调用,因此其实现方法与上述方法相同,也会抛出IllegalAccessError 异常。 + +getSyncAction 方法同样不应该被调用,并且其实现方法与前两个方法类似,会抛出一个IllegalAccessError 异常。 + +这些方法的目的是为了确保程序在运行时不会意外地调用这些方法,从而导致错误发生。如果有人在代码中尝试调用这些方法,将会得到明确的错误提示。*/ \ No newline at end of file diff --git a/doc/蔡玉祥注释的代码/NetworkFailureException.java b/doc/蔡玉祥注释的代码/NetworkFailureException.java new file mode 100644 index 0000000..b08cfb1 --- /dev/null +++ b/doc/蔡玉祥注释的代码/NetworkFailureException.java @@ -0,0 +1,33 @@ +/* + * 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.exception; + +public class NetworkFailureException extends Exception { + private static final long serialVersionUID = 2107610287180234136L; + + public NetworkFailureException() { + super(); + } + + public NetworkFailureException(String paramString) { + super(paramString); + } + + public NetworkFailureException(String paramString, Throwable paramThrowable) { + super(paramString, paramThrowable); + } +} diff --git a/doc/蔡玉祥注释的代码/Node.java b/doc/蔡玉祥注释的代码/Node.java new file mode 100644 index 0000000..44ab2bc --- /dev/null +++ b/doc/蔡玉祥注释的代码/Node.java @@ -0,0 +1,183 @@ +/* + * 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.database.Cursor; + +import org.json.JSONObject; + +public abstract class Node { + public static final int SYNC_ACTION_NONE = 0; + + public static final int SYNC_ACTION_ADD_REMOTE = 1; + + public static final int SYNC_ACTION_ADD_LOCAL = 2; + + public static final int SYNC_ACTION_DEL_REMOTE = 3; + + public static final int SYNC_ACTION_DEL_LOCAL = 4; + + public static final int SYNC_ACTION_UPDATE_REMOTE = 5; + + public static final int SYNC_ACTION_UPDATE_LOCAL = 6; + + public static final int SYNC_ACTION_UPDATE_CONFLICT = 7; + + public static final int SYNC_ACTION_ERROR = 8; + + private String mGid; + + private String mName; + + private long mLastModified; + + private boolean mDeleted; +/*这是一个抽象类 Node 的定义,其中包含以下几个字段和常量: + +字段 mGid:表示节点的全局唯一标识符 gid,每个节点都有一个唯一的 gid。 + +字段 mName:表示节点的名称,该字段为字符串类型。 + +字段 mLastModified:表示节点最后修改的时间戳,以毫秒为单位。 + +字段 mDeleted:表示节点是否被删除(true 表示已删除,false 表示未删除)。 + +常量 SYNC_ACTION_*:这些常量表示同步操作的各种情况。可以使用这些常量来指定要执行的同步操作类型。具体而言,这些常量的含义分别如下: + +SYNC_ACTION_NONE:不进行同步操作; + +SYNC_ACTION_ADD_REMOTE:在远程服务器上添加一个新节点; + +SYNC_ACTION_ADD_LOCAL:在本地数据库中添加一个新节点; + +SYNC_ACTION_DEL_REMOTE:在远程服务器上删除一个节点; + +SYNC_ACTION_DEL_LOCAL:在本地数据库中删除一个节点; + +SYNC_ACTION_UPDATE_REMOTE:在远程服务器上更新一个节点; + +SYNC_ACTION_UPDATE_LOCAL:在本地数据库中更新一个节点; + +SYNC_ACTION_UPDATE_CONFLICT:发生同步冲突,需要解决冲突; + +SYNC_ACTION_ERROR:同步错误,同步失败。 + +此外,Node 类还是一个抽象类,无法直接创建它的实例。所有的节点都是 Node 类的子类,以具体类型的形式继承 Node 类,并提供它们自己的实现。*/ + public Node() { + mGid = null; + mName = ""; + mLastModified = 0; + mDeleted = false; + } +/*这是 Node 类的默认构造方法。该方法会初始化节点的各个字段,包括 mGid、mName、mLastModified 和 mDeleted。 + +具体而言,mGid 字段被初始化为 null,表示此时节点没有被分配一个全局唯一标识符; +mName 字段被初始化为空字符串,表示节点名称为空; +mLastModified 字段被初始化为 0,表示节点最后修改时间的时间戳为 0(即从未修改过); +mDeleted 字段被初始化为 false,表示节点当前未被删除。 + */ + public abstract JSONObject getCreateAction(int actionId); + + public abstract JSONObject getUpdateAction(int actionId); + + public abstract void setContentByRemoteJSON(JSONObject js); + + public abstract void setContentByLocalJSON(JSONObject js); + + public abstract JSONObject getLocalJSONFromContent(); + + public abstract int getSyncAction(Cursor c); +/*这是 Node 抽象类中的一些抽象方法,需要在其子类中进行实现。这些方法分别如下: + +getCreateAction(int actionId):该方法用于生成在远程服务器上创建一个新节点的 JSON 数据。actionId 表示同步操作类型,可以根据不同的同步操作类型来生成对应的 JSON 数据。 + +getUpdateAction(int actionId):该方法用于生成在远程服务器上更新一个节点的 JSON 数据,与 getCreateAction 方法类似,actionId 表示同步操作类型。 + +setContentByRemoteJSON(JSONObject js):该方法用于从远程服务器返回的 JSON 数据中设置节点对象的属性值。 + +setContentByLocalJSON(JSONObject js):该方法用于从本地数据库中读取的 JSON 数据中设置节点对象的属性值。 + +getLocalJSONFromContent():该方法将节点对象转换为本地数据库中的 JSON 形式。 + +getSyncAction(Cursor c):该方法返回表示节点同步操作的常量。 + +这些抽象方法是用于描述哪些数据需要被从远程服务器或本地数据库中读取或写入,并且确定在特定同步操作期间执行的操作类型。这使得 Node 的子类可以根据具体情况对这些方法进行自定义实现,以适应不同场景的需求。*/ + public void setGid(String gid) { + this.mGid = gid; + } +/*这是 Node 类的 setGid 方法,用于设置节点的唯一标识符 gid。 +该方法接收一个 String 类型的参数 gid,表示要为节点设置的全局唯一标识符。 +在执行该方法时,将会将传入的 gid 参数赋值给 mGid 字段,以便在后续的操作中使用该值。 +这个方法可以被其子类调用来设置自己的 gid 值。 + */ + public void setName(String name) { + this.mName = name; + } +/*这是 Node 类的一个方法,用于设置节点的名称 mName 的值。 +它接受一个字符串类型的参数 name,并将它赋值给 mName 字段。 +this 表示当前对象实例。 + */ + public void setLastModified(long lastModified) { + this.mLastModified = lastModified; + } +/*setLastModified具有一个long类型的参数lastModified,表示将该对象的最后修改时间设置为给定值。 +在方法体内,关键字this表示当前对象的引用,即在调用该方法的对象。 +将参数lastModified的值分配给该对象的私有成员变量mLastModified,以便以后可以通过getter方法检索该值。 + */ + public void setDeleted(boolean deleted) { + this.mDeleted = deleted; + } +/*这是一个Java类中的公共方法,名称为setDeleted,它具有一个boolean类型的参数deleted,表示将该对象的删除状态设置为给定的值。 + +在方法体内,关键字this表示当前对象的引用,即在调用该方法的对象。 +将参数deleted的值分配给该对象的私有成员变量mDeleted,以便以后可以通过getter方法检索该值。 +如果参数deleted的值为true,则表示该对象被删除;如果参数deleted的值为false,则表示该对象未被删除。 + */ + public String getGid() { + return this.mGid; + } + + public String getName() { + return this.mName; + } +/*这是一个Java类中的公共方法,名称分别为getGid和getName,它们都不具有任何参数,并返回该对象的私有成员变量mGid和mName的值,分别表示该对象的ID和名称。 + +在方法体内,关键字this表示当前对象的引用,即在调用该方法的对象。 +这些方法通常用于获取对象的状态,而不是修改状态。 +例如,在代码中调用getGid方法将返回该对象的ID,以便在其他操作中使用该ID。 +同样,调用getName方法将返回该对象的名称。 + */ + public long getLastModified() { + return this.mLastModified; + } +/*这是一个Java类中的公共方法,名称为getLastModified,它不具有任何参数,并返回该对象的私有成员变量mLastModified的值,表示该对象的最后修改时间。 + +在方法体内,关键字this表示当前对象的引用,即在调用该方法的对象。 +这个方法通常用于获取对象的状态,以便在其他操作中使用该状态。 +例如,在代码中调用getLastModified方法将返回该对象的最后修改时间,以便在其他操作中使用该时间戳。 + */ + public boolean getDeleted() { + return this.mDeleted; + } +/*这是一个Java类中的公共方法,名称为getDeleted,它不具有任何参数,并返回该对象的私有成员变量mDeleted的值,表示该对象是否被删除。 + +在方法体内,关键字this表示当前对象的引用,即在调用该方法的对象。 +这个方法通常用于获取对象的状态,以便在其他操作中使用该状态。 +例如,在代码中调用getDeleted方法将返回一个boolean类型的值,表示该对象是否被删除。 +如果该值为true,则表示该对象已被删除;如果该值为false,则表示该对象未被删除。 + */ +} diff --git a/doc/蔡玉祥注释的代码/SqlData.java b/doc/蔡玉祥注释的代码/SqlData.java new file mode 100644 index 0000000..e6a6be8 --- /dev/null +++ b/doc/蔡玉祥注释的代码/SqlData.java @@ -0,0 +1,245 @@ +/* + * 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.content.ContentResolver; +import android.content.ContentUris; +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.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.NotesDatabaseHelper.TABLE; +import net.micode.notes.gtask.exception.ActionFailureException; + +import org.json.JSONException; +import org.json.JSONObject; + + +public class SqlData { + private static final String TAG = SqlData.class.getSimpleName(); + + private static final int INVALID_ID = -99999; + + public static final String[] PROJECTION_DATA = new String[] { + DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, + DataColumns.DATA3 + }; +/*这是一个Java类,名称为SqlData,其中定义了一些静态变量和常量,用于在类内和类外引用。 + +第一行定义了一个名为TAG的静态常量字符串,用于在日志中标识该类。这个字符串的值是SqlData类的简单名称。 + +第二行定义了一个名为INVALID_ID的私有静态常量整数,用于表示无效的ID。该值被设置为-99999。 + +第三行定义了一个名为PROJECTION_DATA的公共静态常量字符串数组,用于定义一个查询所需返回的列。这些列包括DataColumns类中定义的ID、MIME_TYPE、CONTENT、DATA1和DATA3。 + +这些变量和常量都被声明为静态的,这意味着它们属于类本身,而不是类的实例。因此,它们可以在类的所有实例之间共享和访问。这些变量和常量的使用可以提高代码的可读性和可维护性,并避免在代码中多次重复相同的值。*/ + public static final int DATA_ID_COLUMN = 0; + + public static final int DATA_MIME_TYPE_COLUMN = 1; + + public static final int DATA_CONTENT_COLUMN = 2; + + public static final int DATA_CONTENT_DATA_1_COLUMN = 3; + + public static final int DATA_CONTENT_DATA_3_COLUMN = 4; + + private ContentResolver mContentResolver; + + private boolean mIsCreate; + + private long mDataId; + + private String mDataMimeType; + + private String mDataContent; + + private long mDataContentData1; + + private String mDataContentData3; + + private ContentValues mDiffDataValues; +/*这是一个Java类,名称为SqlData,其中定义了一些成员变量和常量,用于在类内存储和操作数据。 + +第一行定义了一个名为DATA_ID_COLUMN的公共静态常量整数,用于表示列索引中的数据ID列。 + +第二行定义了一个名为DATA_MIME_TYPE_COLUMN的公共静态常量整数,用于表示列索引中的MIME类型列。 + +第三行定义了一个名为DATA_CONTENT_COLUMN的公共静态常量整数,用于表示列索引中的内容列。 + +第四行定义了一个名为DATA_CONTENT_DATA_1_COLUMN的公共静态常量整数,用于表示列索引中的DATA1列。 + +第五行定义了一个名为DATA_CONTENT_DATA_3_COLUMN的公共静态常量整数,用于表示列索引中的DATA3列。 + +接下来的几行定义了该类的一些成员变量,包括ContentResolver类型的mContentResolver、boolean类型的mIsCreate、long类型的mDataId、String类型的mDataMimeType、String类型的mDataContent、long类型的mDataContentData1、String类型的mDataContentData3和ContentValues类型的mDiffDataValues。这些变量表示了该类的不同数据属性。 + +其中,ContentValues是一个键值对的集合,用于在Android应用中存储和操作数据。在该类中,mDiffDataValues被用于存储该对象的数据属性的差异,即该对象已更改但尚未提交到数据库中的值。*/ + // 构造函数用于初始化一个SqlData对象的实例 + public SqlData(Context context) { + mContentResolver = context.getContentResolver();// 获取ContentResolver对象,用于访问ContentProvider中的数据 + mIsCreate = true;// 初始化数据的创建状态 + mDataId = INVALID_ID; // 初始化数据的ID值 + mDataMimeType = DataConstants.NOTE;// 初始化数据的MIME类型为NOTE + mDataContent = ""; // 初始化数据的内容为空字符串 + mDataContentData1 = 0; // 初始化数据的数据内容Data1为0 + mDataContentData3 = "";// 初始化数据的数据内容Data3为空字符串 + mDiffDataValues = new ContentValues();// 初始化差异数据的ContentValues对象,用于保存数据的不同之处 + } + + // 定义SqlData类,该类用于处理数据库中的数据 + public SqlData(Context context, Cursor c) { + // 获取ContentResolver对象 + mContentResolver = context.getContentResolver(); + // 设置初始状态为非创建状态 + mIsCreate = false; + // 从Cursor对象中读取数据,并将数据加载到 + loadFromCursor(c); + // 创建一个新的ContentValues对象,用于存储数据的差异 + mDiffDataValues = new ContentValues(); + } +/*该构造函数主要是用于初始化SqlData对象,并且在初始化过程中从Cursor对象中读取数据,同时创建一个新的ContentValues对象,以备后续存储数据的差异。*/ +/*定义了一个私有方法loadFromCursor,该方法接收一个Cursor对象作为参数,并且在该方法中从Cursor对象中读取数据,将这些数据加载到SqlData对象中。*/ +// 从Cursor对象中读取数据,并将数据加载到SqlData对象中 + private void loadFromCursor(Cursor c) { + // 从Cursor对象中读取数据id + mDataId = c.getLong(DATA_ID_COLUMN); + // 从Cursor对象中读取数据类型 + mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); + // 从Cursor对象中读取数据内容 + mDataContent = c.getString(DATA_CONTENT_COLUMN); + // 从Cursor对象中读取数据内容的第一个数据项 + mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); + // 从Cursor对象中读取数据内容的第三个数据项 + mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); + } +/*该方法主要是用于从Cursor对象中读取数据,并将这些数据加载到SqlData对象的相关成员变量中。其中,数据的读取顺序和类型需要与Cursor对象中的列名一一对应。*/ + public void setContent(JSONObject js) throws JSONException {// 设置SqlData对象的内容 + long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID;// 从JSONObject对象中获取数据id + if (mIsCreate || mDataId != dataId) { + mDiffDataValues.put(DataColumns.ID, dataId); + } + mDataId = dataId; // 如果当前SqlData对象是创建状态,或者数据id与当前对象的数据id不同,将数据id加入到差异数据值对象中 + +//setContent方法的主要部分,用于设置SqlData对象的各个成员变量值,并且在设置过程中检查差异数据。 + String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) + : DataConstants.NOTE; + if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { + mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); + } + mDataMimeType = dataMimeType; + // 从JSONObject对象中获取数据类型,并将其设置为SqlData对象的数据类型 + String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; + if (mIsCreate || !mDataContent.equals(dataContent)) { + mDiffDataValues.put(DataColumns.CONTENT, dataContent); + } + mDataContent = dataContent; + // 从JSONObject对象中获取数据内容的第一个数据项,并将其设置为SqlData对象的数据内容的第一个数据项 + long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; + if (mIsCreate || mDataContentData1 != dataContentData1) { + mDiffDataValues.put(DataColumns.DATA1, dataContentData1); + } + mDataContentData1 = dataContentData1; + // 从JSONObject对象中获取数据内容的第三个数据项,并将其设置为SqlData对象的数据内容的第三个数据项 + String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; + if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { + mDiffDataValues.put(DataColumns.DATA3, dataContentData3); + } + mDataContentData3 = dataContentData3; + } +/*该代码段主要是根据JSONObject对象中的数据,设置SqlData对象的相应成员变量,并且在设置过程中检查差异数据。 + 其中,如果当前SqlData对象是创建状态,或者相应成员变量的值与JSONObject对象中的值不同,就将相应的数据加入到差异数据值对象中。 + 这些差异数据值将在后续的操作中被用于更新数据库中的数据。*/ + public JSONObject getContent() throws JSONException { + if (mIsCreate) { + Log.e(TAG, "it seems that we haven't created this in database yet"); + return null; + } + JSONObject js = new JSONObject(); + js.put(DataColumns.ID, mDataId); + js.put(DataColumns.MIME_TYPE, mDataMimeType); + js.put(DataColumns.CONTENT, mDataContent); + js.put(DataColumns.DATA1, mDataContentData1); + js.put(DataColumns.DATA3, mDataContentData3); + return js; + } +/*这段代码定义了一个名为getContent()的方法,它返回一个JSONObject对象。 +该方法可能会抛出一个JSONException异常,因此在调用该方法时需要处理该异常。 +方法体内的第一条语句检查一个名为mIsCreate的 boolean 类型的成员变量是否为 true,如果是,则输出一个错误日志并返回 null。否则,方法会创建一个新的JSONObject对象,并向它添加一些键值对,这些键值对是由mDataId、mDataMimeType、mDataContent、mDataContentData1和mDataContentData3这些成员变量组成的。 +最后,该方法返回一个JSONObject对象,其中包含了这些成员变量的值。*/ + + + public void commit(long noteId, boolean validateVersion, long version) { + + if (mIsCreate) { + if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { + mDiffDataValues.remove(DataColumns.ID); + } + + mDiffDataValues.put(DataColumns.NOTE_ID, noteId); + Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); + try { + mDataId = Long.valueOf(uri.getPathSegments().get(1)); + } catch (NumberFormatException e) { + Log.e(TAG, "Get note id error :" + e.toString()); + throw new ActionFailureException("create note failed"); + } + } else { + if (mDiffDataValues.size() > 0) { + int result = 0; + if (!validateVersion) { + result = mContentResolver.update(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null); + } else { + result = mContentResolver.update(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, + " ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { + String.valueOf(noteId), String.valueOf(version) + }); + } + if (result == 0) { + Log.w(TAG, "there is no update. maybe user updates note when syncing"); + } + } + } + + mDiffDataValues.clear(); + mIsCreate = false; + } +/*这段代码定义了一个名为commit()的方法,用于将已经修改的数据保存到数据库中。 +方法接受三个参数:noteId为 long 类型,表示当前笔记的 ID; +validateVersion为 boolean 类型,表示是否需要验证版本; +version为 long 类型,表示当前笔记的版本。 +方法体内的第一条语句检查一个名为mIsCreate的boolean类型的成员变量是否为true,如果是,则表示当前是创建一个新的数据,需要将数据插入到数据库中。 +如果mDiffDataValues中包含 DataColumns.ID 这个键值对,表明该成员变量的值已经被赋值,需要将其移除,因为它是自增的主键,不应该由用户指定。 +接下来,将 DataColumns.NOTE_ID 对应的值设置为noteId,并将mDiffDataValues插入到Notes.CONTENT_DATA_URI中获取Uri对象,然后Uri对象中获取新的数据的 ID 并设置给成员变量mDataId。 +如果获取 ID 失败,则记录错误日志并抛出一个ActionFailureException异常。 +如果mIsCreate的值为 false,则表示需要更新数据库中的数据。 +如果mDiffDataValues中有数据,则调用mContentResolver.update()方法对数据库进行更新。 +如果validateVersion的值为 false,则直接更新数据,否则需要先验证版本。验证版本的过程是通过查询NoteColumns.VERSION等于version且NoteColumns.ID等于的noteId +无论是插入数据还是更新数据,都需要清空mDiffDataValues的内容并将mIsCreate设为 false,表示数据已经提交保存到数据库中。*/ + public long getId() { + return mDataId; + }//定义了一个名为getId()的方法,返回一个long类型的值,表示当前笔记的ID。 + //直接返回成员变量mDataId,因此不会引发任何异常。 +} +6 \ No newline at end of file diff --git a/doc/蔡玉祥注释的代码/SqlNote.java b/doc/蔡玉祥注释的代码/SqlNote.java new file mode 100644 index 0000000..79a4095 --- /dev/null +++ b/doc/蔡玉祥注释的代码/SqlNote.java @@ -0,0 +1,505 @@ +/* + * 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 + }; + + 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; + + 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 mDataList; + + 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(); + } + + public SqlNote(Context context, Cursor c) { + mContext = context; + mContentResolver = context.getContentResolver(); + mIsCreate = false; + loadFromCursor(c); + mDataList = new ArrayList(); + if (mType == Notes.TYPE_NOTE) + loadDataContent(); + mDiffNoteValues = new ContentValues(); + } + + public SqlNote(Context context, long id) { + mContext = context; + mContentResolver = context.getContentResolver(); + mIsCreate = false; + loadFromCursor(id); + mDataList = new ArrayList(); + if (mType == Notes.TYPE_NOTE) + loadDataContent(); + mDiffNoteValues = new ContentValues(); + + } + + 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(); + } + } + + 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); + } + + 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(); + } + } + + 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"); + } 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; + } 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; + + 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; + + 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; + + 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; + + 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; + + 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; + + 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; + + 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; + } + } + } + + if (sqlData == null) { + sqlData = new SqlData(mContext); + mDataList.add(sqlData); + } + + sqlData.setContent(data); + } + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return false; + } + return 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 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); + } 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; + } + + 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; + } + + 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); + } + } + } 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; + } +} diff --git a/doc/蔡玉祥注释的代码/Task.java b/doc/蔡玉祥注释的代码/Task.java new file mode 100644 index 0000000..6a19454 --- /dev/null +++ b/doc/蔡玉祥注释的代码/Task.java @@ -0,0 +1,351 @@ +/* + * 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.database.Cursor; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + + +public class Task extends Node { + private static final String TAG = Task.class.getSimpleName(); + + private boolean mCompleted; + + private String mNotes; + + private JSONObject mMetaInfo; + + private Task mPriorSibling; + + private TaskList mParent; + + public Task() { + super(); + mCompleted = false; + mNotes = null; + mPriorSibling = null; + mParent = null; + mMetaInfo = null; + } + + public JSONObject getCreateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // index + js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this)); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); + entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_TASK); + if (getNotes() != null) { + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); + } + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + // parent_id + js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid()); + + // dest_parent_type + js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_GROUP); + + // list_id + js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid()); + + // prior_sibling_id + if (mPriorSibling != null) { + js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid()); + } + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate task-create jsonobject"); + } + + return js; + } + + public JSONObject getUpdateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // id + js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + if (getNotes() != null) { + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); + } + entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate task-update jsonobject"); + } + + return js; + } + + public void setContentByRemoteJSON(JSONObject js) { + if (js != null) { + try { + // id + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); + } + + // last_modified + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); + } + + // name + if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { + setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); + } + + // notes + if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) { + setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES)); + } + + // deleted + if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) { + setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED)); + } + + // completed + if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) { + setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED)); + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to get task content from jsonobject"); + } + } + } + + public void setContentByLocalJSON(JSONObject js) { + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) + || !js.has(GTaskStringUtils.META_HEAD_DATA)) { + Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); + } + + try { + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + + if (note.getInt(NoteColumns.TYPE) != Notes.TYPE_NOTE) { + Log.e(TAG, "invalid type"); + return; + } + + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i); + if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { + setName(data.getString(DataColumns.CONTENT)); + break; + } + } + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + } + + public JSONObject getLocalJSONFromContent() { + String name = getName(); + try { + if (mMetaInfo == null) { + // new task created from web + if (name == null) { + Log.w(TAG, "the note seems to be an empty one"); + return null; + } + + JSONObject js = new JSONObject(); + JSONObject note = new JSONObject(); + JSONArray dataArray = new JSONArray(); + JSONObject data = new JSONObject(); + data.put(DataColumns.CONTENT, name); + dataArray.put(data); + js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); + note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + js.put(GTaskStringUtils.META_HEAD_NOTE, note); + return js; + } else { + // synced task + JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i); + if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { + data.put(DataColumns.CONTENT, getName()); + break; + } + } + + note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + return mMetaInfo; + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return null; + } + } + + public void setMetaInfo(MetaData metaData) { + if (metaData != null && metaData.getNotes() != null) { + try { + mMetaInfo = new JSONObject(metaData.getNotes()); + } catch (JSONException e) { + Log.w(TAG, e.toString()); + mMetaInfo = null; + } + } + } + + public int getSyncAction(Cursor c) { + try { + JSONObject noteInfo = null; + if (mMetaInfo != null && mMetaInfo.has(GTaskStringUtils.META_HEAD_NOTE)) { + noteInfo = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + } + + if (noteInfo == null) { + Log.w(TAG, "it seems that note meta has been deleted"); + return SYNC_ACTION_UPDATE_REMOTE; + } + + if (!noteInfo.has(NoteColumns.ID)) { + Log.w(TAG, "remote note id seems to be deleted"); + return SYNC_ACTION_UPDATE_LOCAL; + } + + // validate the note id now + if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) { + Log.w(TAG, "note id doesn't match"); + return SYNC_ACTION_UPDATE_LOCAL; + } + + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + // there is no local update + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // no update both side + return SYNC_ACTION_NONE; + } else { + // apply remote to local + return SYNC_ACTION_UPDATE_LOCAL; + } + } else { + // validate gtask id + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + Log.e(TAG, "gtask id doesn't match"); + return SYNC_ACTION_ERROR; + } + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // local modification only + return SYNC_ACTION_UPDATE_REMOTE; + } else { + return SYNC_ACTION_UPDATE_CONFLICT; + } + } + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + + return SYNC_ACTION_ERROR; + } + + public boolean isWorthSaving() { + return mMetaInfo != null || (getName() != null && getName().trim().length() > 0) + || (getNotes() != null && getNotes().trim().length() > 0); + } + + public void setCompleted(boolean completed) { + this.mCompleted = completed; + } + + public void setNotes(String notes) { + this.mNotes = notes; + } + + public void setPriorSibling(Task priorSibling) { + this.mPriorSibling = priorSibling; + } + + public void setParent(TaskList parent) { + this.mParent = parent; + } + + public boolean getCompleted() { + return this.mCompleted; + } + + public String getNotes() { + return this.mNotes; + } + + public Task getPriorSibling() { + return this.mPriorSibling; + } + + public TaskList getParent() { + return this.mParent; + } + +} diff --git a/doc/蔡玉祥注释的代码/TaskList.java b/doc/蔡玉祥注释的代码/TaskList.java new file mode 100644 index 0000000..4ea21c5 --- /dev/null +++ b/doc/蔡玉祥注释的代码/TaskList.java @@ -0,0 +1,343 @@ +/* + * 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.database.Cursor; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + + +public class TaskList extends Node { + private static final String TAG = TaskList.class.getSimpleName(); + + private int mIndex; + + private ArrayList mChildren; + + public TaskList() { + super(); + mChildren = new ArrayList(); + mIndex = 1; + } + + public JSONObject getCreateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // index + js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); + entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_GROUP); + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate tasklist-create jsonobject"); + } + + return js; + } + + public JSONObject getUpdateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // id + js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate tasklist-update jsonobject"); + } + + return js; + } + + public void setContentByRemoteJSON(JSONObject js) { + if (js != null) { + try { + // id + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); + } + + // last_modified + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); + } + + // name + if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { + setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); + } + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to get tasklist content from jsonobject"); + } + } + } + + public void setContentByLocalJSON(JSONObject js) { + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { + Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); + } + + try { + JSONObject folder = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + + if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { + String name = folder.getString(NoteColumns.SNIPPET); + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name); + } else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { + if (folder.getLong(NoteColumns.ID) == Notes.ID_ROOT_FOLDER) + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT); + else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER) + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + + GTaskStringUtils.FOLDER_CALL_NOTE); + else + Log.e(TAG, "invalid system folder"); + } else { + Log.e(TAG, "error type"); + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + } + + public JSONObject getLocalJSONFromContent() { + try { + JSONObject js = new JSONObject(); + JSONObject folder = new JSONObject(); + + String folderName = getName(); + if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)) + folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(), + folderName.length()); + folder.put(NoteColumns.SNIPPET, folderName); + if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) + || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE)) + folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + else + folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + + js.put(GTaskStringUtils.META_HEAD_NOTE, folder); + + return js; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return null; + } + } + + public int getSyncAction(Cursor c) { + try { + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + // there is no local update + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // no update both side + return SYNC_ACTION_NONE; + } else { + // apply remote to local + return SYNC_ACTION_UPDATE_LOCAL; + } + } else { + // validate gtask id + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + Log.e(TAG, "gtask id doesn't match"); + return SYNC_ACTION_ERROR; + } + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // local modification only + return SYNC_ACTION_UPDATE_REMOTE; + } else { + // for folder conflicts, just apply local modification + return SYNC_ACTION_UPDATE_REMOTE; + } + } + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + + return SYNC_ACTION_ERROR; + } + + public int getChildTaskCount() { + return mChildren.size(); + } + + public boolean addChildTask(Task task) { + boolean ret = false; + if (task != null && !mChildren.contains(task)) { + ret = mChildren.add(task); + if (ret) { + // need to set prior sibling and parent + task.setPriorSibling(mChildren.isEmpty() ? null : mChildren + .get(mChildren.size() - 1)); + task.setParent(this); + } + } + return ret; + } + + public boolean addChildTask(Task task, int index) { + if (index < 0 || index > mChildren.size()) { + Log.e(TAG, "add child task: invalid index"); + return false; + } + + int pos = mChildren.indexOf(task); + if (task != null && pos == -1) { + mChildren.add(index, task); + + // update the task list + Task preTask = null; + Task afterTask = null; + if (index != 0) + preTask = mChildren.get(index - 1); + if (index != mChildren.size() - 1) + afterTask = mChildren.get(index + 1); + + task.setPriorSibling(preTask); + if (afterTask != null) + afterTask.setPriorSibling(task); + } + + return true; + } + + public boolean removeChildTask(Task task) { + boolean ret = false; + int index = mChildren.indexOf(task); + if (index != -1) { + ret = mChildren.remove(task); + + if (ret) { + // reset prior sibling and parent + task.setPriorSibling(null); + task.setParent(null); + + // update the task list + if (index != mChildren.size()) { + mChildren.get(index).setPriorSibling( + index == 0 ? null : mChildren.get(index - 1)); + } + } + } + return ret; + } + + public boolean moveChildTask(Task task, int index) { + + if (index < 0 || index >= mChildren.size()) { + Log.e(TAG, "move child task: invalid index"); + return false; + } + + int pos = mChildren.indexOf(task); + if (pos == -1) { + Log.e(TAG, "move child task: the task should in the list"); + return false; + } + + if (pos == index) + return true; + return (removeChildTask(task) && addChildTask(task, index)); + } + + public Task findChildTaskByGid(String gid) { + for (int i = 0; i < mChildren.size(); i++) { + Task t = mChildren.get(i); + if (t.getGid().equals(gid)) { + return t; + } + } + return null; + } + + public int getChildTaskIndex(Task task) { + return mChildren.indexOf(task); + } + + public Task getChildTaskByIndex(int index) { + if (index < 0 || index >= mChildren.size()) { + Log.e(TAG, "getTaskByIndex: invalid index"); + return null; + } + return mChildren.get(index); + } + + public Task getChilTaskByGid(String gid) { + for (Task task : mChildren) { + if (task.getGid().equals(gid)) + return task; + } + return null; + } + + public ArrayList getChildTaskList() { + return this.mChildren; + } + + public void setIndex(int index) { + this.mIndex = index; + } + + public int getIndex() { + return this.mIndex; + } +} diff --git a/src/.idea/deploymentTargetDropDown.xml b/src/.idea/deploymentTargetDropDown.xml index d07e8ff..152075b 100644 --- a/src/.idea/deploymentTargetDropDown.xml +++ b/src/.idea/deploymentTargetDropDown.xml @@ -12,10 +12,18 @@ +<<<<<<< HEAD <<<<<<< HEAD:src/.idea/deploymentTargetDropDown.xml ======= >>>>>>> cyx_branch:.idea/deploymentTargetDropDown.xml +======= +<<<<<<< HEAD:.idea/deploymentTargetDropDown.xml + +======= + +>>>>>>> master:src/.idea/deploymentTargetDropDown.xml +>>>>>>> 25eb5a04c09307c5edd9569e8d491a90234c9d82 \ No newline at end of file diff --git a/src/app/src/main/java/net/micode/notes/gtask/data/SqlData.java b/src/app/src/main/java/net/micode/notes/gtask/data/SqlData.java index 6801c6a..04001de 100644 --- a/src/app/src/main/java/net/micode/notes/gtask/data/SqlData.java +++ b/src/app/src/main/java/net/micode/notes/gtask/data/SqlData.java @@ -45,11 +45,22 @@ public class SqlData { DataColumns.DATA3 }; /*这是一个Java类,名称为SqlData,其中定义了一些静态变量和常量,用于在类内和类外引用。 +<<<<<<< HEAD 第一行定义了一个名为TAG的静态常量字符串,用于在日志中标识该类。这个字符串的值是SqlData类的简单名称。 第二行定义了一个名为INVALID_ID的私有静态常量整数,用于表示无效的ID。该值被设置为-99999。 第三行定义了一个名为PROJECTION_DATA的公共静态常量字符串数组,用于定义一个查询所需返回的列。这些列包括DataColumns类中定义的ID、MIME_TYPE、CONTENT、DATA1和DATA3。 这些变量和常量都被声明为静态的,这意味着它们属于类本身,而不是类的实例。因此,它们可以在类的所有实例之间共享和访问。 这些变量和常量的使用可以提高代码的可读性和可维护性,并避免在代码中多次重复相同的值。*/ +======= + +第一行定义了一个名为TAG的静态常量字符串,用于在日志中标识该类。这个字符串的值是SqlData类的简单名称。 + +第二行定义了一个名为INVALID_ID的私有静态常量整数,用于表示无效的ID。该值被设置为-99999。 + +第三行定义了一个名为PROJECTION_DATA的公共静态常量字符串数组,用于定义一个查询所需返回的列。这些列包括DataColumns类中定义的ID、MIME_TYPE、CONTENT、DATA1和DATA3。 + +这些变量和常量都被声明为静态的,这意味着它们属于类本身,而不是类的实例。因此,它们可以在类的所有实例之间共享和访问。这些变量和常量的使用可以提高代码的可读性和可维护性,并避免在代码中多次重复相同的值。*/ +>>>>>>> 25eb5a04c09307c5edd9569e8d491a90234c9d82 public static final int DATA_ID_COLUMN = 0; public static final int DATA_MIME_TYPE_COLUMN = 1; @@ -239,3 +250,4 @@ version为 long 类型,表示当前笔记的版本。 }//定义了一个名为getId()的方法,返回一个long类型的值,表示当前笔记的ID。 //直接返回成员变量mDataId,因此不会引发任何异常。 } +6 \ No newline at end of file