use of com.ichi2.utils.JSONArray in project AnkiChinaAndroid by ankichinateam.
the class Finder method _findTemplate.
private String _findTemplate(String val) {
// were we given an ordinal number?
Integer num = null;
try {
num = Integer.parseInt(val) - 1;
} catch (NumberFormatException e) {
num = null;
}
if (num != null) {
return "c.ord = " + num;
}
// search for template names
List<String> lims = new ArrayList<>();
for (JSONObject m : mCol.getModels().all()) {
JSONArray tmpls = m.getJSONArray("tmpls");
for (int ti = 0; ti < tmpls.length(); ++ti) {
JSONObject t = tmpls.getJSONObject(ti);
String templateName = t.getString("name");
Normalizer.normalize(templateName, Normalizer.Form.NFC);
if (templateName.equalsIgnoreCase(val)) {
if (m.getInt("type") == Consts.MODEL_CLOZE) {
// if the user has asked for a cloze card, we want
// to give all ordinals, so we just limit to the
// model instead
lims.add("(n.mid = " + m.getLong("id") + ")");
} else {
lims.add("(n.mid = " + m.getLong("id") + " and c.ord = " + t.getInt("ord") + ")");
}
}
}
}
return TextUtils.join(" or ", lims.toArray(new String[lims.size()]));
}
use of com.ichi2.utils.JSONArray in project AnkiChinaAndroid by ankichinateam.
the class Finder method _findField.
private String _findField(String field, String val) {
/*
* We need two expressions to query the cards: One that will use JAVA REGEX syntax and another
* that should use SQLITE LIKE clause syntax.
*/
String sqlVal = val.replace("%", // For SQLITE, we escape all % signs
"\\%").replace("*", // And then convert the * into non-escaped % signs
"%");
/*
* The following three lines make sure that only _ and * are valid wildcards.
* Any other characters are enclosed inside the \Q \E markers, which force
* all meta-characters in between them to lose their special meaning
*/
String javaVal = val.replace("_", "\\E.\\Q").replace("*", "\\E.*\\Q");
/*
* For the pattern, we use the javaVal expression that uses JAVA REGEX syntax
*/
Pattern pattern = Pattern.compile("\\Q" + javaVal + "\\E", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
// find models that have that field
Map<Long, Object[]> mods = new HashMap<>();
for (JSONObject m : mCol.getModels().all()) {
JSONArray flds = m.getJSONArray("flds");
for (int fi = 0; fi < flds.length(); ++fi) {
JSONObject f = flds.getJSONObject(fi);
String fieldName = f.getString("name");
fieldName = Normalizer.normalize(fieldName, Normalizer.Form.NFC);
if (fieldName.equalsIgnoreCase(field)) {
mods.put(m.getLong("id"), new Object[] { m, f.getInt("ord") });
}
}
}
if (mods.isEmpty()) {
// nothing has that field
return null;
}
LinkedList<Long> nids = new LinkedList<>();
try (Cursor cur = mCol.getDb().getDatabase().query("select id, mid, flds from notes where mid in " + Utils.ids2str(new LinkedList<>(mods.keySet())) + " and flds like ? escape '\\'", new String[] { "%" + sqlVal + "%" })) {
while (cur.moveToNext()) {
String[] flds = Utils.splitFields(cur.getString(2));
int ord = (Integer) mods.get(cur.getLong(1))[1];
String strg = flds[ord];
if (pattern.matcher(strg).matches()) {
nids.add(cur.getLong(0));
}
}
}
if (nids.isEmpty()) {
return "0";
}
return "n.id in " + Utils.ids2str(nids);
}
use of com.ichi2.utils.JSONArray in project AnkiChinaAndroid by ankichinateam.
the class Finder method fieldNames.
public List<String> fieldNames(Collection col, boolean downcase) {
Set<String> fields = new HashSet<>();
List<String> names = new ArrayList<>();
for (JSONObject m : col.getModels().all()) {
JSONArray flds = m.getJSONArray("flds");
for (int fi = 0; fi < flds.length(); ++fi) {
JSONObject f = flds.getJSONObject(fi);
if (!fields.contains(f.getString("name").toLowerCase(Locale.US))) {
names.add(f.getString("name"));
fields.add(f.getString("name").toLowerCase(Locale.US));
}
}
}
if (downcase) {
return new ArrayList<>(fields);
}
return names;
}
use of com.ichi2.utils.JSONArray in project AnkiChinaAndroid by ankichinateam.
the class Finder method ordForMid.
/**
* Find duplicates
* ***********************************************************
*/
public static Integer ordForMid(Collection col, Map<Long, Integer> fields, long mid, String fieldName) {
if (!fields.containsKey(mid)) {
JSONObject model = col.getModels().get(mid);
JSONArray flds = model.getJSONArray("flds");
for (int c = 0; c < flds.length(); c++) {
JSONObject f = flds.getJSONObject(c);
if (f.getString("name").equalsIgnoreCase(fieldName)) {
fields.put(mid, c);
break;
}
}
}
return fields.get(mid);
}
use of com.ichi2.utils.JSONArray in project AnkiChinaAndroid by ankichinateam.
the class Media method mediaChangesZip.
/**
* Media syncing: zips
* ***********************************************************
*/
/**
* Unlike python, our temp zip file will be on disk instead of in memory. This avoids storing
* potentially large files in memory which is not feasible with Android's limited heap space.
* <p>
* Notes:
* <p>
* - The maximum size of the changes zip is decided by the constant SYNC_ZIP_SIZE. If a media file exceeds this
* limit, only that file (in full) will be zipped to be sent to the server.
* <p>
* - This method will be repeatedly called from MediaSyncer until there are no more files (marked "dirty" in the DB)
* to send.
* <p>
* - Since AnkiDroid avoids scanning the media folder on every sync, it is possible for a file to be marked as a
* new addition but actually have been deleted (e.g., with a file manager). In this case we skip over the file
* and mark it as removed in the database. (This behaviour differs from the desktop client).
* <p>
*/
public Pair<File, List<String>> mediaChangesZip() {
File f = new File(mCol.getPath().replaceFirst("collection\\.anki2$", "tmpSyncToServer.zip"));
Cursor cur = null;
try (ZipOutputStream z = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)))) {
z.setMethod(ZipOutputStream.DEFLATED);
List<String> fnames = new ArrayList<>();
// meta is a list of (fname, zipname), where zipname of null is a deleted file
// NOTE: In python, meta is a list of tuples that then gets serialized into json and added
// to the zip as a string. In our version, we use JSON objects from the start to avoid the
// serialization step. Instead of a list of tuples, we use JSONArrays of JSONArrays.
JSONArray meta = new JSONArray();
int sz = 0;
byte[] buffer = new byte[2048];
cur = mDb.getDatabase().query("select fname, csum from media where dirty=1 limit " + Consts.SYNC_ZIP_COUNT, null);
for (int c = 0; cur.moveToNext(); c++) {
String fname = cur.getString(0);
String csum = cur.getString(1);
fnames.add(fname);
String normname = Utils.nfcNormalized(fname);
if (!TextUtils.isEmpty(csum)) {
try {
mCol.log("+media zip " + fname);
File file = new File(dir(), fname);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file), 2048);
z.putNextEntry(new ZipEntry(Integer.toString(c)));
int count = 0;
while ((count = bis.read(buffer, 0, 2048)) != -1) {
z.write(buffer, 0, count);
}
z.closeEntry();
bis.close();
meta.put(new JSONArray().put(normname).put(Integer.toString(c)));
sz += file.length();
} catch (FileNotFoundException e) {
// A file has been marked as added but no longer exists in the media directory.
// Skip over it and mark it as removed in the db.
removeFile(fname);
}
} else {
mCol.log("-media zip " + fname);
meta.put(new JSONArray().put(normname).put(""));
}
if (sz >= Consts.SYNC_ZIP_SIZE) {
break;
}
}
z.putNextEntry(new ZipEntry("_meta"));
z.write(Utils.jsonToString(meta).getBytes());
z.closeEntry();
// Don't leave lingering temp files if the VM terminates.
f.deleteOnExit();
return new Pair<>(f, fnames);
} catch (IOException e) {
Timber.e(e, "Failed to create media changes zip: ");
throw new RuntimeException(e);
} finally {
if (cur != null) {
cur.close();
}
}
}
Aggregations