Deleted unused classes related to modification (#3084)

Removed ModificationsSyncAdapter
This commit is contained in:
Ilgaz Er 2019-07-23 16:44:07 +03:00 committed by Ashish Kumar
parent 75f3098c35
commit 386d08794e
14 changed files with 4 additions and 872 deletions

View file

@ -162,17 +162,6 @@
android:name="android.content.SyncAdapter"
android:resource="@xml/contributions_sync_adapter" />
</service>
<service
android:name=".modifications.ModificationsSyncService"
android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/modifications_sync_adapter" />
</service>
<service
android:name="org.acra.sender.SenderService"
@ -194,12 +183,7 @@
android:exported="false"
android:label="@string/provider_contributions"
android:syncable="true" />
<provider
android:name=".modifications.ModificationsContentProvider"
android:authorities="${applicationId}.modifications.contentprovider"
android:exported="false"
android:label="@string/provider_modifications"
android:syncable="true" />
<provider
android:name=".category.CategoryContentProvider"
android:authorities="${applicationId}.categories.contentprovider"

View file

@ -10,6 +10,8 @@ import android.os.Build;
import android.os.Process;
import android.util.Log;
import androidx.annotation.NonNull;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.squareup.leakcanary.LeakCanary;
@ -28,7 +30,6 @@ import java.io.File;
import javax.inject.Inject;
import javax.inject.Named;
import androidx.annotation.NonNull;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
@ -41,7 +42,6 @@ import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.logging.FileLoggingTree;
import fr.free.nrw.commons.logging.LogUtils;
import fr.free.nrw.commons.modifications.ModifierSequenceDao;
import fr.free.nrw.commons.upload.FileUtils;
import fr.free.nrw.commons.utils.ConfigUtils;
import io.reactivex.android.schedulers.AndroidSchedulers;
@ -265,7 +265,6 @@ public class CommonsApplication extends Application {
dbOpenHelper.getReadableDatabase().close();
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
ModifierSequenceDao.Table.onDelete(db);
CategoryDao.Table.onDelete(db);
ContributionDao.Table.onDelete(db);
BookmarkPicturesDao.Table.onDelete(db);

View file

@ -9,7 +9,6 @@ import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
import fr.free.nrw.commons.category.CategoryDao;
import fr.free.nrw.commons.contributions.ContributionDao;
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao;
import fr.free.nrw.commons.modifications.ModifierSequenceDao;
public class DBOpenHelper extends SQLiteOpenHelper {
@ -27,7 +26,6 @@ public class DBOpenHelper extends SQLiteOpenHelper {
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
ContributionDao.Table.onCreate(sqLiteDatabase);
ModifierSequenceDao.Table.onCreate(sqLiteDatabase);
CategoryDao.Table.onCreate(sqLiteDatabase);
BookmarkPicturesDao.Table.onCreate(sqLiteDatabase);
BookmarkLocationsDao.Table.onCreate(sqLiteDatabase);
@ -37,7 +35,6 @@ public class DBOpenHelper extends SQLiteOpenHelper {
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int from, int to) {
ContributionDao.Table.onUpdate(sqLiteDatabase, from, to);
ModifierSequenceDao.Table.onUpdate(sqLiteDatabase, from, to);
CategoryDao.Table.onUpdate(sqLiteDatabase, from, to);
BookmarkPicturesDao.Table.onUpdate(sqLiteDatabase, from, to);
BookmarkLocationsDao.Table.onUpdate(sqLiteDatabase, from, to);

View file

@ -1,6 +1,5 @@
package fr.free.nrw.commons.di;
import fr.free.nrw.commons.contributions.ContributionsModule;
import javax.inject.Singleton;
import dagger.Component;
@ -10,8 +9,8 @@ import dagger.android.support.AndroidSupportInjectionModule;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.contributions.ContributionViewHolder;
import fr.free.nrw.commons.contributions.ContributionsModule;
import fr.free.nrw.commons.contributions.ContributionsSyncAdapter;
import fr.free.nrw.commons.modifications.ModificationsSyncAdapter;
import fr.free.nrw.commons.nearby.PlaceRenderer;
import fr.free.nrw.commons.review.ReviewController;
import fr.free.nrw.commons.settings.SettingsFragment;
@ -36,8 +35,6 @@ public interface CommonsApplicationComponent extends AndroidInjector<Application
void inject(ContributionsSyncAdapter syncAdapter);
void inject(ModificationsSyncAdapter syncAdapter);
void inject(LoginActivity activity);
void inject(SettingsFragment fragment);

View file

@ -7,7 +7,6 @@ import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider;
import fr.free.nrw.commons.category.CategoryContentProvider;
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider;
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
@Module
@SuppressWarnings({"WeakerAccess", "unused"})
@ -16,9 +15,6 @@ public abstract class ContentProviderBuilderModule {
@ContributesAndroidInjector
abstract ContributionsContentProvider bindContributionsContentProvider();
@ContributesAndroidInjector
abstract ModificationsContentProvider bindModificationsContentProvider();
@ContributesAndroidInjector
abstract CategoryContentProvider bindCategoryContentProvider();

View file

@ -1,48 +0,0 @@
package fr.free.nrw.commons.modifications;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class CategoryModifier extends PageModifier {
public static String PARAM_CATEGORIES = "categories";
public static String MODIFIER_NAME = "CategoriesModifier";
public CategoryModifier(String... categories) {
super(MODIFIER_NAME);
JSONArray categoriesArray = new JSONArray();
for (String category: categories) {
categoriesArray.put(category);
}
try {
params.putOpt(PARAM_CATEGORIES, categoriesArray);
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
public CategoryModifier(JSONObject data) {
super(MODIFIER_NAME);
this.params = data;
}
@Override
public String doModification(String pageName, String pageContents) {
JSONArray categories;
categories = params.optJSONArray(PARAM_CATEGORIES);
StringBuilder categoriesString = new StringBuilder();
for (int i = 0; i < categories.length(); i++) {
String category = categories.optString(i);
categoriesString.append("\n[[Category:").append(category).append("]]");
}
return pageContents + categoriesString.toString();
}
@Override
public String getEditSummary() {
return "Added " + params.optJSONArray(PARAM_CATEGORIES).length() + " categories.";
}
}

View file

@ -1,161 +0,0 @@
package fr.free.nrw.commons.modifications;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import androidx.annotation.NonNull;
import android.text.TextUtils;
import javax.inject.Inject;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.data.DBOpenHelper;
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
import timber.log.Timber;
import static fr.free.nrw.commons.modifications.ModifierSequenceDao.Table.TABLE_NAME;
public class ModificationsContentProvider extends CommonsDaggerContentProvider {
private static final int MODIFICATIONS = 1;
private static final int MODIFICATIONS_ID = 2;
public static final String BASE_PATH = "modifications";
public static final Uri BASE_URI = Uri.parse("content://" + BuildConfig.MODIFICATION_AUTHORITY + "/" + BASE_PATH);
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
uriMatcher.addURI(BuildConfig.MODIFICATION_AUTHORITY, BASE_PATH, MODIFICATIONS);
uriMatcher.addURI(BuildConfig.MODIFICATION_AUTHORITY, BASE_PATH + "/#", MODIFICATIONS_ID);
}
public static Uri uriForId(int id) {
return Uri.parse(BASE_URI.toString() + "/" + id);
}
@Inject DBOpenHelper dbOpenHelper;
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(TABLE_NAME);
int uriType = uriMatcher.match(uri);
switch (uriType) {
case MODIFICATIONS:
break;
default:
throw new IllegalArgumentException("Unknown URI" + uri);
}
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Override
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
long id;
switch (uriType) {
case MODIFICATIONS:
id = sqlDB.insert(TABLE_NAME, null, contentValues);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(BASE_URI + "/" + id);
}
@Override
public int delete(@NonNull Uri uri, String s, String[] strings) {
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
switch (uriType) {
case MODIFICATIONS_ID:
String id = uri.getLastPathSegment();
sqlDB.delete(TABLE_NAME,
"_id = ?",
new String[] { id }
);
return 1;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
}
@Override
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
Timber.d("Hello, bulk insert! (ModificationsContentProvider)");
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
sqlDB.beginTransaction();
switch (uriType) {
case MODIFICATIONS:
for (ContentValues value: values) {
Timber.d("Inserting! %s", value);
sqlDB.insert(TABLE_NAME, null, value);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
sqlDB.setTransactionSuccessful();
sqlDB.endTransaction();
getContext().getContentResolver().notifyChange(uri, null);
return values.length;
}
@Override
public int update(@NonNull Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
/*
SQL Injection warnings: First, note that we're not exposing this to the outside world (exported="false")
Even then, we should make sure to sanitize all user input appropriately. Input that passes through ContentValues
should be fine. So only issues are those that pass in via concating.
In here, the only concat created argument is for id. It is cast to an int, and will error out otherwise.
*/
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
int rowsUpdated;
switch (uriType) {
case MODIFICATIONS:
rowsUpdated = sqlDB.update(TABLE_NAME,
contentValues,
selection,
selectionArgs);
break;
case MODIFICATIONS_ID:
int id = Integer.valueOf(uri.getLastPathSegment());
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(TABLE_NAME,
contentValues,
ModifierSequenceDao.Table.COLUMN_ID + " = ?",
new String[] { String.valueOf(id) } );
} else {
throw new IllegalArgumentException("Parameter `selection` should be empty when updating an ID");
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri + " with type " + uriType);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}
}

View file

@ -1,126 +0,0 @@
package fr.free.nrw.commons.modifications;
import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.SyncResult;
import android.database.Cursor;
import android.os.Bundle;
import android.os.RemoteException;
import java.io.IOException;
import javax.inject.Inject;
import javax.inject.Named;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.actions.PageEditClient;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.contributions.ContributionDao;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import timber.log.Timber;
public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
@Inject MediaWikiApi mwApi;
@Inject ContributionDao contributionDao;
@Inject ModifierSequenceDao modifierSequenceDao;
@Inject
SessionManager sessionManager;
@Inject
@Named("commons-page-edit")
PageEditClient commonsPageEditClient;
public ModificationsSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}
@Override
public void onPerformSync(Account account, Bundle bundle, String s, ContentProviderClient contentProviderClient, SyncResult syncResult) {
// This code is fraught with possibilities of race conditions, but lalalalala I can't hear you!
ApplicationlessInjection
.getInstance(getContext()
.getApplicationContext())
.getCommonsApplicationComponent()
.inject(this);
Cursor allModifications;
try {
allModifications = contentProviderClient.query(ModificationsContentProvider.BASE_URI, null, null, null, null);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
// Exit early if nothing to do
if (allModifications == null || allModifications.getCount() == 0) {
Timber.d("No modifications to perform");
return;
}
allModifications.moveToFirst();
Timber.d("Found %d modifications to execute", allModifications.getCount());
ContentProviderClient contributionsClient = null;
try {
contributionsClient = getContext().getContentResolver().acquireContentProviderClient(BuildConfig.CONTRIBUTION_AUTHORITY);
while (!allModifications.isAfterLast()) {
ModifierSequence sequence = modifierSequenceDao.fromCursor(allModifications);
Contribution contrib;
Cursor contributionCursor;
if (contributionsClient == null) {
Timber.e("ContributionsClient is null. This should not happen!");
return;
}
try {
contributionCursor = contributionsClient.query(sequence.getMediaUri(), null, null, null, null);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
if (contributionCursor != null) {
contributionCursor.moveToFirst();
}
contrib = contributionDao.fromCursor(contributionCursor);
if (contrib != null && contrib.getState() == Contribution.STATE_COMPLETED) {
String pageContent;
try {
pageContent = mwApi.revisionsByFilename(contrib.getFilename());
} catch (IOException e) {
Timber.d("Network messed up on modifications sync!");
continue;
}
Timber.d("Page content is %s", pageContent);
String processedPageContent = sequence.executeModifications(contrib.getFilename(), pageContent);
Disposable disposable = commonsPageEditClient
.edit(contrib.getFilename(), processedPageContent, sequence.getEditSummary())
.subscribe(editResult -> {
if (!editResult) {
Timber.d("Non success result!");
} else {
modifierSequenceDao.delete(sequence);
}
});
}
allModifications.moveToNext();
}
} finally {
if (contributionsClient != null) {
contributionsClient.release();
}
}
}
}

View file

@ -1,27 +0,0 @@
package fr.free.nrw.commons.modifications;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class ModificationsSyncService extends Service {
private static final Object sSyncAdapterLock = new Object();
private static ModificationsSyncAdapter sSyncAdapter = null;
@Override
public void onCreate() {
super.onCreate();
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new ModificationsSyncAdapter(this, true);
}
}
}
@Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
}

View file

@ -1,64 +0,0 @@
package fr.free.nrw.commons.modifications;
import android.net.Uri;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
public class ModifierSequence {
private Uri mediaUri;
private ArrayList<PageModifier> modifiers;
private Uri contentUri;
public ModifierSequence(Uri mediaUri) {
this.mediaUri = mediaUri;
modifiers = new ArrayList<>();
}
ModifierSequence(Uri mediaUri, JSONObject data) {
this(mediaUri);
JSONArray modifiersJSON = data.optJSONArray("modifiers");
for (int i = 0; i < modifiersJSON.length(); i++) {
modifiers.add(PageModifier.fromJSON(modifiersJSON.optJSONObject(i)));
}
}
Uri getMediaUri() {
return mediaUri;
}
public void queueModifier(PageModifier modifier) {
modifiers.add(modifier);
}
String executeModifications(String pageName, String pageContents) {
for (PageModifier modifier: modifiers) {
pageContents = modifier.doModification(pageName, pageContents);
}
return pageContents;
}
String getEditSummary() {
StringBuilder editSummary = new StringBuilder();
for (PageModifier modifier: modifiers) {
editSummary.append(modifier.getEditSummary()).append(" ");
}
editSummary.append("Using [[COM:MOA|Commons Mobile App]]");
return editSummary.toString();
}
ArrayList<PageModifier> getModifiers() {
return modifiers;
}
Uri getContentUri() {
return contentUri;
}
void setContentUri(Uri contentUri) {
this.contentUri = contentUri;
}
}

View file

@ -1,124 +0,0 @@
package fr.free.nrw.commons.modifications;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.RemoteException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
public class ModifierSequenceDao {
private final Provider<ContentProviderClient> clientProvider;
@Inject
public ModifierSequenceDao(@Named("modification") Provider<ContentProviderClient> clientProvider) {
this.clientProvider = clientProvider;
}
public void save(ModifierSequence sequence) {
ContentProviderClient db = clientProvider.get();
try {
if (sequence.getContentUri() == null) {
sequence.setContentUri(db.insert(ModificationsContentProvider.BASE_URI, toContentValues(sequence)));
} else {
db.update(sequence.getContentUri(), toContentValues(sequence), null, null);
}
} catch (RemoteException e) {
throw new RuntimeException(e);
} finally {
db.release();
}
}
public void delete(ModifierSequence sequence) {
ContentProviderClient db = clientProvider.get();
try {
db.delete(sequence.getContentUri(), null, null);
} catch (RemoteException e) {
throw new RuntimeException(e);
} finally {
db.release();
}
}
ModifierSequence fromCursor(Cursor cursor) {
// Hardcoding column positions!
ModifierSequence ms;
try {
ms = new ModifierSequence(Uri.parse(cursor.getString(cursor.getColumnIndex(Table.COLUMN_MEDIA_URI))),
new JSONObject(cursor.getString(cursor.getColumnIndex(Table.COLUMN_DATA))));
} catch (JSONException e) {
throw new RuntimeException(e);
}
ms.setContentUri( ModificationsContentProvider.uriForId(cursor.getInt(cursor.getColumnIndex(Table.COLUMN_ID))));
return ms;
}
private JSONObject toJSON(ModifierSequence sequence) {
JSONObject data = new JSONObject();
try {
JSONArray modifiersJSON = new JSONArray();
for (PageModifier modifier: sequence.getModifiers()) {
modifiersJSON.put(modifier.toJSON());
}
data.put("modifiers", modifiersJSON);
return data;
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
private ContentValues toContentValues(ModifierSequence sequence) {
ContentValues cv = new ContentValues();
cv.put(Table.COLUMN_MEDIA_URI, sequence.getMediaUri().toString());
cv.put(Table.COLUMN_DATA, toJSON(sequence).toString());
return cv;
}
public static class Table {
static final String TABLE_NAME = "modifications";
static final String COLUMN_ID = "_id";
static final String COLUMN_MEDIA_URI = "mediauri";
static final String COLUMN_DATA = "data";
// NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
public static final String[] ALL_FIELDS = {
COLUMN_ID,
COLUMN_MEDIA_URI,
COLUMN_DATA
};
static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;
static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
+ "_id INTEGER PRIMARY KEY,"
+ "mediauri STRING,"
+ "data STRING"
+ ");";
public static void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_STATEMENT);
}
public static void onUpdate(SQLiteDatabase db, int from, int to) {
db.execSQL(DROP_TABLE_STATEMENT);
onCreate(db);
}
public static void onDelete(SQLiteDatabase db) {
db.execSQL(DROP_TABLE_STATEMENT);
onCreate(db);
}
}
}

View file

@ -1,41 +0,0 @@
package fr.free.nrw.commons.modifications;
import org.json.JSONException;
import org.json.JSONObject;
public abstract class PageModifier {
public static PageModifier fromJSON(JSONObject data) {
String name = data.optString("name");
if (name.equals(CategoryModifier.MODIFIER_NAME)) {
return new CategoryModifier(data.optJSONObject("data"));
} else if (name.equals(TemplateRemoveModifier.MODIFIER_NAME)) {
return new TemplateRemoveModifier(data.optJSONObject("data"));
}
return null;
}
protected String name;
protected JSONObject params;
protected PageModifier(String name) {
this.name = name;
params = new JSONObject();
}
public abstract String doModification(String pageName, String pageContents);
public abstract String getEditSummary();
public JSONObject toJSON() {
JSONObject data = new JSONObject();
try {
data.putOpt("name", name);
data.put("data", params);
} catch (JSONException e) {
throw new RuntimeException(e);
}
return data;
}
}

View file

@ -1,94 +0,0 @@
package fr.free.nrw.commons.modifications;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TemplateRemoveModifier extends PageModifier {
public static final String MODIFIER_NAME = "TemplateRemoverModifier";
public static final String PARAM_TEMPLATE_NAME = "template";
public static final Pattern PATTERN_TEMPLATE_OPEN = Pattern.compile("\\{\\{");
public static final Pattern PATTERN_TEMPLATE_CLOSE = Pattern.compile("\\}\\}");
public TemplateRemoveModifier(String templateName) {
super(MODIFIER_NAME);
try {
params.putOpt(PARAM_TEMPLATE_NAME, templateName);
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
public TemplateRemoveModifier(JSONObject data) {
super(MODIFIER_NAME);
this.params = data;
}
@Override
public String doModification(String pageName, String pageContents) {
String templateRawName = params.optString(PARAM_TEMPLATE_NAME);
// Wikitext title normalizing rules. Spaces and _ equivalent
// They also 'condense' - any number of them reduce to just one (just like HTML)
String templateNormalized = templateRawName.trim().replaceAll("(\\s|_)+", "(\\s|_)+");
// Not supporting {{ inside <nowiki> and HTML comments yet
// (Thanks to marktraceur for reminding me of the HTML comments exception)
Pattern templateStartPattern = Pattern.compile("\\{\\{" + templateNormalized, Pattern.CASE_INSENSITIVE);
Matcher matcher = templateStartPattern.matcher(pageContents);
while (matcher.find()) {
int braceCount = 1;
int startIndex = matcher.start();
int curIndex = matcher.end();
Matcher openMatch = PATTERN_TEMPLATE_OPEN.matcher(pageContents);
Matcher closeMatch = PATTERN_TEMPLATE_CLOSE.matcher(pageContents);
while (curIndex < pageContents.length()) {
boolean openFound = openMatch.find(curIndex);
boolean closeFound = closeMatch.find(curIndex);
if (openFound && (!closeFound || openMatch.start() < closeMatch.start())) {
braceCount++;
curIndex = openMatch.end();
} else if (closeFound) {
braceCount--;
curIndex = closeMatch.end();
} else if (braceCount > 0) {
// The template never closes, so...remove nothing
curIndex = startIndex;
break;
}
if (braceCount == 0) {
// The braces have all been closed!
break;
}
}
// Strip trailing whitespace
while (curIndex < pageContents.length()) {
if (pageContents.charAt(curIndex) == ' ' || pageContents.charAt(curIndex) == '\n') {
curIndex++;
} else {
break;
}
}
// I am so going to hell for this, sigh
pageContents = pageContents.substring(0, startIndex) + pageContents.substring(curIndex);
matcher = templateStartPattern.matcher(pageContents);
}
return pageContents;
}
@Override
public String getEditSummary() {
return "Removed template " + params.optString(PARAM_TEMPLATE_NAME) + ".";
}
}

View file

@ -1,156 +0,0 @@
package fr.free.nrw.commons.modifications
import android.content.ContentProviderClient
import android.content.ContentValues
import android.database.MatrixCursor
import android.database.sqlite.SQLiteDatabase
import android.net.Uri
import android.os.RemoteException
import com.nhaarman.mockito_kotlin.*
import fr.free.nrw.commons.BuildConfig
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.modifications.ModificationsContentProvider.BASE_URI
import fr.free.nrw.commons.modifications.ModifierSequenceDao.Table.*
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [21], application = TestCommonsApplication::class)
class ModifierSequenceDaoTest {
private val mediaUrl = "http://example.com/"
private val columns = arrayOf(COLUMN_ID, COLUMN_MEDIA_URI, COLUMN_DATA)
private val client: ContentProviderClient = mock()
private val database: SQLiteDatabase = mock()
private val contentValuesCaptor = argumentCaptor<ContentValues>()
private lateinit var testObject: ModifierSequenceDao
@Before
fun setUp() {
testObject = ModifierSequenceDao { client }
}
@Test
fun createFromCursorWithEmptyModifiers() {
testObject.fromCursor(createCursor("")).let {
assertEquals(mediaUrl, it.mediaUri.toString())
assertEquals(BASE_URI.buildUpon().appendPath("1").toString(), it.contentUri.toString())
assertTrue(it.modifiers.isEmpty())
}
}
@Test
fun createFromCursorWtihCategoryModifier() {
val cursor = createCursor("{\"name\": \"CategoriesModifier\", \"data\": {}}")
val seq = testObject.fromCursor(cursor)
assertEquals(1, seq.modifiers.size)
assertTrue(seq.modifiers[0] is CategoryModifier)
}
@Test
fun createFromCursorWithRemoveModifier() {
val cursor = createCursor("{\"name\": \"TemplateRemoverModifier\", \"data\": {}}")
val seq = testObject.fromCursor(cursor)
assertEquals(1, seq.modifiers.size)
assertTrue(seq.modifiers[0] is TemplateRemoveModifier)
}
@Test
fun deleteSequence() {
whenever(client.delete(isA(), isNull(), isNull())).thenReturn(1)
val seq = testObject.fromCursor(createCursor(""))
testObject.delete(seq)
verify(client).delete(eq(seq.contentUri), isNull(), isNull())
}
@Test(expected = RuntimeException::class)
fun deleteTranslatesRemoteExceptions() {
whenever(client.delete(isA(), isNull(), isNull())).thenThrow(RemoteException(""))
val seq = testObject.fromCursor(createCursor(""))
testObject.delete(seq)
}
@Test
fun saveExistingSequence() {
val modifierJson = "{\"name\":\"CategoriesModifier\",\"data\":{}}"
val expectedData = "{\"modifiers\":[$modifierJson]}"
val cursor = createCursor(modifierJson)
val seq = testObject.fromCursor(cursor)
testObject.save(seq)
verify(client).update(eq(seq.contentUri), contentValuesCaptor.capture(), isNull(), isNull())
contentValuesCaptor.firstValue.let {
assertEquals(2, it.size())
assertEquals(mediaUrl, it.get(COLUMN_MEDIA_URI))
assertEquals(expectedData, it.get(COLUMN_DATA))
}
}
@Test
fun saveNewSequence() {
val expectedContentUri = BASE_URI.buildUpon().appendPath("1").build()
whenever(client.insert(isA(), isA())).thenReturn(expectedContentUri)
val seq = ModifierSequence(Uri.parse(mediaUrl))
testObject.save(seq)
assertEquals(expectedContentUri.toString(), seq.contentUri.toString())
verify(client).insert(eq(ModificationsContentProvider.BASE_URI), contentValuesCaptor.capture())
contentValuesCaptor.firstValue.let {
assertEquals(2, it.size())
assertEquals(mediaUrl, it.get(COLUMN_MEDIA_URI))
assertEquals("{\"modifiers\":[]}", it.get(COLUMN_DATA))
}
}
@Test(expected = RuntimeException::class)
fun saveTranslatesRemoteExceptions() {
whenever(client.insert(isA(), isA())).thenThrow(RemoteException(""))
testObject.save(ModifierSequence(Uri.parse(mediaUrl)))
}
@Test
fun createTable() {
onCreate(database)
verify(database).execSQL(CREATE_TABLE_STATEMENT)
}
@Test
fun updateTable() {
onUpdate(database, 1, 2)
inOrder(database) {
verify<SQLiteDatabase>(database).execSQL(DROP_TABLE_STATEMENT)
verify<SQLiteDatabase>(database).execSQL(CREATE_TABLE_STATEMENT)
}
}
@Test
fun deleteTable() {
onDelete(database)
inOrder(database) {
verify<SQLiteDatabase>(database).execSQL(DROP_TABLE_STATEMENT)
verify<SQLiteDatabase>(database).execSQL(CREATE_TABLE_STATEMENT)
}
}
private fun createCursor(modifierJson: String) = MatrixCursor(columns, 1).apply {
addRow(listOf("1", mediaUrl, "{\"modifiers\": [$modifierJson]}"))
moveToFirst()
}
}