greenrobot / greendao Goto Github PK
View Code? Open in Web Editor NEWgreenDAO is a light & fast ORM solution for Android that maps objects to SQLite databases.
Home Page: http://greenrobot.org/greendao/
greenDAO is a light & fast ORM solution for Android that maps objects to SQLite databases.
Home Page: http://greenrobot.org/greendao/
Annoying boiler plate code is just perfect for geenDAO...
Would be nice to take load of the main thread and to exploit multi-cores better.
To be decided: The API could be based on Futures or on callback interface. Or a combination?
Today "dates" are stored/read as Long(CurrentTimeMillis).
We have an application which have a synchronization api created by Oracle.
In this database the "dates" are stored as a string. ("2008-04-24 09:10:00.0")
This string needs to be parsed into a java Date.
So a way to set the format of Dates is what is needed.
Regards Thomas
See http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html
Performs better with mixed capitals and umlauts.
Are you using greenDAO? Please add a comment to this issue with a link to your app, so we can it off on a new greenDAO web page.
My onFailure
callbacks in JsonHttpResponseHandler
are never called.
I have to overwrite handleFailureMessage
to handle connectivity problems.
Additionally, which of the onFailure
methods will be called? All of them or only the ones I overwrite?
A debug switch (session-scoped?) to enable logging of all SQL statements.
Currently PKs are limited to long/Long although other types are prepared at some places.
Security - Array is stored directly
The user-supplied array 'recordData' is stored directly.
this.id = id;
this.recordData = recordData;
}
recordData is a Byte Array.
Security - Array is stored directly
Plugin: pmd Key: ArrayIsStoredDirectly
Constructors and methods receiving arrays should clone objects and store the copy. This prevents that future changes from the user affect the internal functionality.
Hello
Town is my entity, I use setter to set coordinate and then update(or insert), but it occurs exception
double[] coordinate = Connection.getTownCoordinate(townId);
town.setLatitude(coordinate[0]);
town.setLongitude(coordinate[1]);
town.update();
10-03 00:06:01.301: E/AndroidRuntime(1821): Caused by: java.lang.ClassCastException: java.lang.Integer
10-03 00:06:01.301: E/AndroidRuntime(1821): at de.greenrobot.dao.IdentityScopeLong.put(IdentityScopeLong.java:1)
10-03 00:06:01.301: E/AndroidRuntime(1821): at de.greenrobot.dao.AbstractDao.attachEntity(AbstractDao.java:570)
10-03 00:06:01.301: E/AndroidRuntime(1821): at de.greenrobot.dao.AbstractDao.updateKeyAfterInsertAndAttach(AbstractDao.java:314)
10-03 00:06:01.301: E/AndroidRuntime(1821): at de.greenrobot.dao.AbstractDao.executeInsert(AbstractDao.java:308)
10-03 00:06:01.301: E/AndroidRuntime(1821): at de.greenrobot.dao.AbstractDao.insertOrReplace(AbstractDao.java:299)
10-03 00:06:01.301: E/AndroidRuntime(1821): at tw.kewang.smile319.Main$LoadStoreTask.doInBackground(Main.java:348)
10-03 00:06:01.301: E/AndroidRuntime(1821): at tw.kewang.smile319.Main$LoadStoreTask.doInBackground(Main.java:1)
and My Generator is written
town = schema.addEntity("Town");
town.addIntProperty("id").primaryKey();
town.addStringProperty("name").notNull();
town.addDoubleProperty("latitude");
town.addDoubleProperty("longitude");
Property cityId = town.addIntProperty("cityId").notNull().getProperty();
ToMany cityToTowns = city.addToMany(town, cityId);
cityToTowns.setName("towns");
If I want to add some data, I only use
townDao.insertWithoutSettingPk(new Town(townId++, elemTown.text(), null, null, cityId));
can't use
townDao.insert(new Town(townId++, elemTown.text(), null, null, cityId));
During this discussion https://plus.google.com/112858355176935102950/posts/hk22JHnMwnf some people said they liked the notifications of the ContentProvider framework. It's actually a nice idea for greenDAO as well.
Unlike #2 this ticket is not about entities, but about adding listeners not the entity itself.
First thoughts:
(A)Synchronous:
Old, notes - probably too complicated:
~~We should adapt JPA stuff like described here: http://www.objectdb.com/java/jpa/persistence/event#Listeners_and_External_Callback_Methods_
How the API could look like:
See https://groups.google.com/forum/#!topic/greendao/cbbUkP6DdHs for a discussion.
Using SQLite's DEFAULT values probably won't work because greenDAO bind's all values for inserting.
Probably, the values should be set in the constructor
Check if the synchronization of identity scope modification and transaction can be aligned better. This may increase consistency a bit in parallel situations e.g. when just deleted entities can still be loaded for a short period. Maybe we could lock the identity scope before starting the transaction. However this may lead to reduced performance. Also there has to be solid multithreaded unit testing and deadlock checking.
Generate for each to-many relation in the entity:
Those methods keep objects and persisted DB rows in sync.
It keeps bugging people to get this right by hand, it seems.
android ships with FTS3 support (FTS4 on honeycomb +)
it would be nice to support FTS tables to provide better full text search.
it might be also good to support adding SQL triggers automatically from entity tables.
details:
http://www.sqlite.org/fts3.html
Greetings,
Recently we decided to use greenDAO in our project.
After implementing DAO layer and covering it with UT - we created EMMA instrumented build.
Every AbstractDaoTestLongPk or AbstractDaoSessionTest based UT classes fail with such error:
[INFO] End: com.xxxxxxxxxx.dao.SharedListTest#testAssignPk
[INFO] Start: com.xxxxxxxxxx.dao.SharedListTest#testCount
[INFO] ERROR:com.xxxxxxxxxx.dao.SharedListTest#testCount
[INFO] java.lang.RuntimeException: Could not prepare DAO Test
at de.greenrobot.dao.test.AbstractDaoTest.setUp(AbstractDaoTest.java:71)
at de.greenrobot.dao.test.AbstractDaoTestSinglePk.setUp(AbstractDaoTestSinglePk.java:56)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:529)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1448)
Caused by: de.greenrobot.dao.DaoException: Could not init DAOConfig
at de.greenrobot.dao.DaoConfig.(DaoConfig.java:88)
at de.greenrobot.dao.UnitTestDaoAccess.(UnitTestDaoAccess.java:30)
at de.greenrobot.dao.test.AbstractDaoTest.setUp(AbstractDaoTest.java:68)
... 10 more
Caused by: java.lang.IllegalAccessException: access to field not allowed
at java.lang.reflect.Field.getField(Native Method)
at java.lang.reflect.Field.get(Field.java:237)
at de.greenrobot.dao.DaoConfig.reflectProperties(DaoConfig.java:98)
at de.greenrobot.dao.DaoConfig.(DaoConfig.java:51)
... 12 more
After some research it appears that EMMA adds synthetic fields into the instrumented classes, and this I found the source of the issue: DaoConfig.reflectProperties method.
I have a fix ready for the issue.
Aka composite/compound keys.
It has to be decided how to address the composite key in Java. Probably with a composite key class like in JPA?
SQLCipher has its own native libs and classes.
http://sqlcipher.net/sqlcipher-for-android/
For me its a common use case to synch a local database with a datastore on App Engine and I dont want to use any framework like objectify.
So i need methods to map Datastore Entities to POJOs.
It would be nice to be able to automatically generate something like this and the corresponding static Variables:
public Record(Entity entity) {
try {
this.id = (Long) entity.getProperty(ID);
} catch (ClassCastException e) {}
try {
this.recordTitle = (String) entity.getProperty(TITLE);
} catch (ClassCastException e) {}
try {
this.recordDescription = (String) entity.getProperty(DESCRIPTION);
} catch (ClassCastException e) {}
try {
this.created = (Date) entity.getProperty(CREATED);
} catch (ClassCastException e) {}
try {
this.recordDataBlobStoreKey = (String) entity.getProperty(DATA_BLOB_STORE_KEY);
} catch (ClassCastException e) {}
}
public Entity toDataStoreEntity() {
Entity entity = new Entity(recordEntityName);
entity.setProperty(CREATED, this.created);
entity.setProperty(DESCRIPTION, this.recordDescription);
entity.setProperty(DATA_BLOB_STORE_KEY, this.recordDataBlobStoreKey);
entity.setProperty(TITLE, this.recordTitle);
entity.setProperty(ID, this.id);
return entity;
}
I could extend the generator myself, if you would just give me some hints about which steps are needed and which classes should be modified to achieve that. I dont want to break your design.
Probably the generator could have a Method generateDatastoreConvinience(), so i could just enable and disable the feature?
Example: getTargetEntityList instead of getTargetEntity to state it is List that is returned.
This might break existing code, but is easy to fix.
createSqlSelectCountStar
in SqlUtils
ignores tablePrefix
variable.
so if you run a count query with where conditions, it does not run properly.
example to reproduce:
QueryBuilder<User> qb = getUserDao().queryBuilder();
qb.whereOr(UserDao.Properties.Property1.eq(true), UserDao.Properties.Property2.eq(false));
return qb.buildCount().count();
this creates a query like
SELECT COUNT(*) FROM USER WHERE (T.'PROPERTY1'=0 OR T.'PROPERTY2'=1)
which fails because column T.'PROPERTY1
does not exist.
instead it should create
SELECT COUNT(*) FROM USER T WHERE (T.'PROPERTY1'=0 OR T.'PROPERTY2'=1)
i think it can easily be fixed by sending tablePrefix
to createSqlSelectCountStar
Add support for specify limit and offset to QueryBuilder and Query.
The current example project is very limited. Either extend it to show more features of greenDAO, or provide a second example project (and keep the existing example simple).
Hello!
I guess a good addition would be to implement the serializable interface inside the entities, cause you are able to push entities through intents by using putExtra(String, Serializable).
Or is there today a possibility to configure the generator using some interfaces? I only found to implement super-classes.
Thanks, Markus for this great and simple OR Mapper.
Kind Regards,
Thomas
An automatic approach to update (maybe even create) the database schema. Using the registered DAOs, we could figure out how the database should look like. Given the current state of the database it should be possible to create what's missing, drop what's too much, and change what differs.
Open questions:
Would it be possible to add a NOT IN where condition.
I use this sometimes for excluding already read rows etc..
At the moment I am doing:
mRandomFoodQuery.where(new WhereCondition.PropertyCondition(FoodDao.Properties.Id,
" NOT IN " + mSb.toString()));
"mSb.toString()" is the generated (1,2,3,4) string generated for the query.
Of course this works fine, but I guess im loosing performance of generating the query all the time instead of having a cached one which I can replace Where values.
Cheers,
Chris
sample code:
WhereCondition whereSomeEq = Properties.IsActive.eq(Boolean.TRUE);
Query query = someDao.queryBuilder().where(whereSomeEq).build();
query.setParameter(0, Boolean.TRUE);
List results = query.list();
expected result:
in my case, not empty list of some objects
real result:
empty list, because Query.setParameter(int, Object)
sets any object as String
without any convertations like this: WhereCondition.PropertyCondition.checkValueForType(Property, Object)
This is a minor change, and I'm hoping fairly easy to do. (if i get a chance I'll do a pull request).
But would it be possible to set the transient field modifier on none dao properties?
E.g:
private Long id;
private Integer eventId;
private String name;
/** Used to resolve relations */
private DaoSession daoSession;
/** Used for active entity operations. */
private EventsDao myDao;
Would become:
private Long id;
private Integer eventId;
private String name;
/** Used to resolve relations */
private transient DaoSession daoSession;
/** Used for active entity operations. */
private transient EventsDao myDao;
Why?
Some JSON and XML parsers will ignore trying to parse transient fields. Which in this case is a good thing, as GSON would try to look for (and fail) to parse the daoSession and myDao fields.
Seen as this modifier has no side effects (and would also help people who are, wrongly, serialising the models), I can't see why this should be an issue?
Cheers,
Chris
if FK == PK throw exception
See https://groups.google.com/d/topic/greendao/rEmhe_NqFM8/discussion
Provide an import of existing databases. Via a standard Java standard main application and JDBC would could support all sorts of databases, e.g. server databases as well.
Hi,
I am using greenDao.When I am updating row using update() method I am getting Datatype mismatch error.Please tell me the solution.
QueryBuilder is nice but requires some additional tasks, e.g. managing the Query, setting parameters, etc.
Example:
Query query = entity.addQuery("ByNameWithMinAge");
query.eq(nameProperty);
query.gt(agePropery);
would generate in the DAO:
List queryByNameWithMinAge(String name, int age)
or with query.unique():
Entity queryByNameWithMinAge(String name, int age)
Plus convenience for simple "equals" query:
entity.addQueryEquals("ByNameWithMinAge", nameProperty);
Addition delete by's would be a nice addition.
deleteByProperty(WhereCondition cond);
deleteByRaw(String whereCond);
We want to clear data out of the database using " WHERE fieldname < datetime('now')"
SQLite on android does not support deleting with LIMIT and ORDER parameters but this can be provided by re-writing query with an in statement.
It would be nice if buildDelete
method in QueryBuilder
would automatically do this re-write if there is a limit or offset inside the query. (right now it ignores them silently, which may cause bugs if developer is not careful)
To be precise:
SQLite on Android does not support this:
DELETE FROM books ORDER BY created ASC LIMIT 10; #delete 10 oldest books
but it can be re-written as
DELETE FROM books WHERE _id IN (SELECT _id FROM books ORDER BY created ASC LIMIT 10 );
which will do the same thing.
When trying to collect the code coverage of a project using emma or the eclemma plugin for eclipse, unit tests including greenDAO functionality crash with the following stacktrace:
de.greenrobot.dao.DaoException
at de.greenrobot.dao.DaoConfig.(DaoConfig.java:88)
at de.greenrobot.dao.AbstractDaoMaster.registerDaoClass(AbstractDaoMaster.java:43)
at com.example.db.DaoMaster.(DaoMaster.java:74)
at com.example.TestApplication.onCreate(TestApplication.java:159)
at com.example.test.Test.setup(Test.java:49)
at com.xtremelabs.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:284)
Caused by: java.lang.IllegalAccessException: Class de.greenrobot.dao.DaoConfig can not access a member of class com.example.db.AuthenticationDao$Properties with modifiers "private static final"
at java.lang.reflect.Field.doSecurityCheck(Field.java:960)
at java.lang.reflect.Field.getFieldAccessor(Field.java:896)
at java.lang.reflect.Field.get(Field.java:358)
at de.greenrobot.dao.DaoConfig.reflectProperties(DaoConfig.java:98)
at de.greenrobot.dao.DaoConfig.(DaoConfig.java:51)
The problem seems to be that emma (and as far as I know also other code coverage tools) add additional properties to the classes they instrument.
If an entity that implements Parcelable is passed from one Activity to another and the Parcelable class is not implemented correctly (not sending the id property) the update method fails without a clear failure message.
See stack trace:
11-07 16:54:43.114: E/AndroidRuntime(21655): FATAL EXCEPTION: main
11-07 16:54:43.114: E/AndroidRuntime(21655): java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { (has extras) }} to activity {com.z/com.z.areas.ActivityGeo}: java.lang.NullPointerException
11-07 16:54:43.114: E/AndroidRuntime(21655): at android.app.ActivityThread.deliverResults(ActivityThread.java:2504)
11-07 16:54:43.114: E/AndroidRuntime(21655): at android.app.ActivityThread.handleSendResult(ActivityThread.java:2546)
11-07 16:54:43.114: E/AndroidRuntime(21655): at android.app.ActivityThread.access$2000(ActivityThread.java:121)
11-07 16:54:43.114: E/AndroidRuntime(21655): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:966)
11-07 16:54:43.114: E/AndroidRuntime(21655): at android.os.Handler.dispatchMessage(Handler.java:99)
11-07 16:54:43.114: E/AndroidRuntime(21655): at android.os.Looper.loop(Looper.java:123)
11-07 16:54:43.114: E/AndroidRuntime(21655): at android.app.ActivityThread.main(ActivityThread.java:3652)
11-07 16:54:43.114: E/AndroidRuntime(21655): at java.lang.reflect.Method.invokeNative(Native Method)
11-07 16:54:43.114: E/AndroidRuntime(21655): at java.lang.reflect.Method.invoke(Method.java:507)
11-07 16:54:43.114: E/AndroidRuntime(21655): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:862)
11-07 16:54:43.114: E/AndroidRuntime(21655): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)
11-07 16:54:43.114: E/AndroidRuntime(21655): at dalvik.system.NativeStart.main(Native Method)
11-07 16:54:43.114: E/AndroidRuntime(21655): Caused by: java.lang.NullPointerException
11-07 16:54:43.114: E/AndroidRuntime(21655): at de.greenrobot.dao.AbstractDao.updateInsideSynchronized(AbstractDao.java:444)
11-07 16:54:43.114: E/AndroidRuntime(21655): at de.greenrobot.dao.AbstractDao.update(AbstractDao.java:428)
11-07 16:54:43.114: E/AndroidRuntime(21655): at com.z.areas.Area.update(Area.java:131)
11-07 16:54:43.114: E/AndroidRuntime(21655): at com.z.areas.ActivityGeo.onActivityResult(ActivityGeo.java:137)
11-07 16:54:43.114: E/AndroidRuntime(21655): at android.app.Activity.dispatchActivityResult(Activity.java:3907)
11-07 16:54:43.114: E/AndroidRuntime(21655): at android.app.ActivityThread.deliverResults(ActivityThread.java:2500)
11-07 16:54:43.114: E/AndroidRuntime(21655): ... 11 more
Hi,
So far greenDAO is very cool, and I'm wondering if there's any future plan in support generating enum type properties, which are essentially ints.
Possible example:
(DaoGenerator class)
..
enum MyEnumType { TYPE1, TYPE2 }
..
Entity ent = schema.addEntity("MyEntity");
ent.addEnumProperty("enumProperty", MyEnumType);
..
which generates these things in code.
More efficient than querying all entities.
Also as preparation for a better delete query considering the identity scope...
Callbacks for the entity class itself to organize its data.
The JPA equivalent would be the following annotations (http://en.wikibooks.org/wiki/Java_Persistence/Advanced_Topics#Events), which we should should adapt:
Thoughts on how to implement:
Open questions:
This tickets relates to #44 (external listeners).
Pre-populating a database can be currently done by copying a database or executing a SQL script.
But copying a database is not always possible and the SQL script is slow.
Provide a BulkImporter Class that parses a CSV file, checks&matches the column names, and insert it into the database using a compiled statement.
My table contains a column name called "from", which is a reserved keyword in SQL. greenDAO is escaping the column
name (T.'FROM') in the select part but not in the where part. The result is
an exception, see below.
01-27 11:13:25.125: E/MainActivity_(8951):
android.database.sqlite.SQLiteException: near "FROM": syntax error: , while
compiling: SELECT T.'id',T.'FROM',T.'TO' FROM GUIDANCE T WHERE T.FROM=?
AND T.TO=?
01-27 11:13:25.125: E/MainActivity(8951): at
android.database.sqlite.SQLiteCompiledSql.native_compile(Native Method)
01-27 11:13:25.125: E/MainActivity_(8951): at
android.database.sqlite.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:92)
01-27 11:13:25.125: E/MainActivity_(8951): at
android.database.sqlite.SQLiteCompiledSql.(SQLiteCompiledSql.java:65)
01-27 11:13:25.125: E/MainActivity_(8951): at
android.database.sqlite.SQLiteProgram.(SQLiteProgram.java:83)
01-27 11:13:25.125: E/MainActivity_(8951): at
android.database.sqlite.SQLiteQuery.(SQLiteQuery.java:49)
01-27 11:13:25.125: E/MainActivity_(8951): at
android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:42)
01-27 11:13:25.125: E/MainActivity_(8951): at
android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1467)
01-27 11:13:25.125: E/MainActivity_(8951): at
android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1435)
01-27 11:13:25.125: E/MainActivity_(8951): at
de.greenrobot.dao.Query.unique(Query.java:108)
01-27 11:13:25.125: E/MainActivity_(8951): at
de.greenrobot.dao.QueryBuilder.unique(QueryBuilder.java:284)
01-27 11:13:25.125: E/MainActivity_(8951): at
info.collide.android.mobilemaze.MainActivity.fetchLocationFromRepository(MainActivity.java:225)
When you add
entity.addIntProperty("order");
it will generate some code without any errors. Unfortunately the SQL generated by the querybuilder will throw an Exception since ORDER is a reserved keyword. Maybe greenDAO should throw an Exception in the generator when a reserved word is used.
I am trying to test a project that uses greenDAO with Robolectric (http://pivotal.github.com/robolectric/). However, when calling loadAll on a generated DAO class, loadAll crashes with a NullPointerException.
The reason for this is that the method protected List loadAllFromCursor(Cursor cursor) in class AbstractDAO is assuming the CursorWindow of a CrossProcessCursor to be always not null. When adding a null pointer check to this method, the loadAll method works fine also with Robolectric.
E.g. do something like this.
if (cursor instanceof CrossProcessCursor) {
CursorWindow window = ((CrossProcessCursor) cursor).getWindow();
if (window != null) {
if(window.getNumRows() == count) {
cursor = new FastCursor(window);
} else {
DaoLog.d("Window vs. result size: " + window.getNumRows() + "/" + count);
}
}
}
If necessary, I can provide you a test project.
Hi,
I've just cloned the repository and got everything running (core, generator and tests). All is fine, but the testOrderUmlauts test is failing. The test says something with umlauts, but my eclipse shows me:
TestEntity entityV = addEntity(list, "V");
TestEntity entityB = addEntity(list, "B");
TestEntity entityUE = addEntity(list, "‹");
TestEntity entityAE = addEntity(list, "ƒ");
Maybe some encoding trouble, since I can't see any Umlauts. This was the error message:
junit.framework.AssertionFailedError: expected:<4> but was:<3>
at de.greenrobot.daotest.query.QueryBuilderOrderTest.testOrderUmlauts(QueryBuilderOrderTest.java:90)
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:529)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1447)
I hope it helps.
Currently, to- one relations must use the key property (long) of the target entity. Make them as flexible as to-many relations.
See also:
https://groups.google.com/forum/#!topic/greendao/Jl2nVLbnCp0
insertOrReplace is already implemented. This is fine unless there are extra columns, which are NOT mapped to the entity. Data in those extra columns will be lost on REPLACE. This may be a special case, e.g. when there are two entities mapped to a single table or the entity has just some of the table's columns.
Implementation brainstorming:
Open questions:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.