2017-10-07 20:13:46 +00:00
|
|
|
--[[--
|
|
|
|
Handles append-mostly data such as KOReader's bookmarks and dictionary search history.
|
|
|
|
]]
|
|
|
|
|
|
|
|
local LuaSettings = require("luasettings")
|
|
|
|
local dump = require("dump")
|
2022-09-27 23:10:50 +00:00
|
|
|
local lfs = require("libs/libkoreader-lfs")
|
2017-10-07 20:13:46 +00:00
|
|
|
local logger = require("logger")
|
|
|
|
local util = require("util")
|
|
|
|
|
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
|
|
|
local LuaData = LuaSettings:extend{
|
2017-10-07 20:13:46 +00:00
|
|
|
name = "",
|
|
|
|
max_backups = 9,
|
|
|
|
}
|
|
|
|
|
|
|
|
--- Creates a new LuaData instance.
|
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
|
|
|
function LuaData:open(file_path, name)
|
|
|
|
-- Backwards compat, just in case...
|
|
|
|
if type(name) == "table" then
|
|
|
|
name = name.name
|
2017-10-07 20:13:46 +00:00
|
|
|
end
|
|
|
|
|
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
|
|
|
-- NOTE: Beware, our new instance is new, but self is still LuaData!
|
|
|
|
local new = LuaData:extend{
|
|
|
|
name = name,
|
|
|
|
file = file_path,
|
|
|
|
data = {},
|
|
|
|
}
|
2017-10-07 20:13:46 +00:00
|
|
|
|
2022-09-27 23:10:50 +00:00
|
|
|
-- Some magic to allow for self-describing function names:
|
|
|
|
-- We'll use data_env both as the environment when loading the data, *and* its metatable,
|
|
|
|
-- *and* as the target of its index lookup metamethod.
|
|
|
|
-- Its NameEntry field is a function responsible for actually storing the data in the right place in the LuaData object.
|
|
|
|
-- It gets called via __index lookup in the global scope (i.e., the env) when Lua tries to resolve
|
|
|
|
-- the global NameEntry function calls in our stored data.
|
|
|
|
-- NOTE: We could also make the metatable's __index field point to a function, and handle the lookup ourselves inside it,
|
|
|
|
-- but using an empty env with loadfile is not a bad idea to begin with anyway ;).
|
|
|
|
local data_env = {}
|
|
|
|
data_env.__index = data_env
|
|
|
|
setmetatable(data_env, data_env)
|
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
|
|
|
data_env[new.name.."Entry"] = function(table)
|
2017-10-07 20:13:46 +00:00
|
|
|
if table.index then
|
2022-09-27 23:10:50 +00:00
|
|
|
-- We've got a deleted setting, overwrite with nil and be done with it.
|
|
|
|
if not table.data then
|
|
|
|
new.data[table.index] = nil
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
if type(table.data) == "table" then
|
|
|
|
new.data[table.index] = new.data[table.index] or {}
|
|
|
|
local size = util.tableSize(table.data)
|
|
|
|
if size == 1 then
|
|
|
|
-- It's an incremental array element, insert it in the array at its proper index
|
|
|
|
for key, value in pairs(table.data) do
|
|
|
|
new.data[table.index][key] = value
|
|
|
|
end
|
|
|
|
else
|
|
|
|
-- It's a complex table, just replace the whole thing
|
|
|
|
new.data[table.index] = table.data
|
2017-10-07 20:13:46 +00:00
|
|
|
end
|
|
|
|
else
|
|
|
|
new.data[table.index] = table.data
|
|
|
|
end
|
|
|
|
else
|
2022-09-27 23:10:50 +00:00
|
|
|
-- It's an untagged blob, use it as-is
|
2017-10-07 20:13:46 +00:00
|
|
|
new.data = table
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-09-27 23:10:50 +00:00
|
|
|
local ok, err
|
2019-12-10 22:00:08 +00:00
|
|
|
if lfs.attributes(new.file, "mode") == "file" then
|
2022-09-27 23:10:50 +00:00
|
|
|
ok, err = loadfile(new.file, "t", data_env)
|
2019-12-10 22:00:08 +00:00
|
|
|
if ok then
|
2022-10-09 17:32:35 +00:00
|
|
|
logger.dbg("LuaData: data is read from", new.file)
|
2022-09-27 23:10:50 +00:00
|
|
|
ok()
|
2019-12-10 22:00:08 +00:00
|
|
|
else
|
2022-10-09 17:32:35 +00:00
|
|
|
logger.dbg("LuaData:", new.file, "is invalid, removed.", err)
|
2019-12-10 22:00:08 +00:00
|
|
|
os.remove(new.file)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if not ok then
|
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
|
|
|
for i=1, new.max_backups, 1 do
|
2017-10-07 20:13:46 +00:00
|
|
|
local backup_file = new.file..".old."..i
|
2019-12-10 22:00:08 +00:00
|
|
|
if lfs.attributes(backup_file, "mode") == "file" then
|
2022-09-27 23:10:50 +00:00
|
|
|
ok, err = loadfile(backup_file, "t", data_env)
|
|
|
|
if ok then
|
2022-10-09 17:32:35 +00:00
|
|
|
logger.dbg("LuaData: data is read from", backup_file)
|
2022-09-27 23:10:50 +00:00
|
|
|
ok()
|
2019-12-10 22:00:08 +00:00
|
|
|
break
|
|
|
|
else
|
2022-10-09 17:32:35 +00:00
|
|
|
logger.dbg("LuaData:", backup_file, "is invalid, removed.", err)
|
2019-12-10 22:00:08 +00:00
|
|
|
os.remove(backup_file)
|
|
|
|
end
|
2017-10-07 20:13:46 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
|
|
|
return new
|
2017-10-07 20:13:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
--- Saves a setting.
|
|
|
|
function LuaData:saveSetting(key, value)
|
|
|
|
self.data[key] = value
|
|
|
|
self:append{
|
|
|
|
index = key,
|
|
|
|
data = value,
|
|
|
|
}
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Deletes a setting.
|
|
|
|
function LuaData:delSetting(key)
|
|
|
|
self.data[key] = nil
|
|
|
|
self:append{
|
|
|
|
index = key,
|
|
|
|
}
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Adds item to table.
|
|
|
|
function LuaData:addTableItem(table_name, value)
|
|
|
|
local settings_table = self:has(table_name) and self:readSetting(table_name) or {}
|
|
|
|
table.insert(settings_table, value)
|
|
|
|
self.data[table_name] = settings_table
|
|
|
|
self:append{
|
|
|
|
index = table_name,
|
|
|
|
data = {[#settings_table] = value},
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Removes index from table.
|
|
|
|
function LuaData:removeTableItem(key, index)
|
2022-09-27 23:10:50 +00:00
|
|
|
LuaSettings.removeTableItem(self, key, index)
|
2017-10-07 20:13:46 +00:00
|
|
|
self:flush()
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Appends settings to disk.
|
|
|
|
function LuaData:append(data)
|
|
|
|
if not self.file then return end
|
|
|
|
local f_out = io.open(self.file, "a")
|
|
|
|
if f_out ~= nil then
|
2022-09-27 23:10:50 +00:00
|
|
|
-- NOTE: This is a function call, with a table as its single argument. Parentheses are elided.
|
2017-10-07 20:13:46 +00:00
|
|
|
f_out:write(self.name.."Entry")
|
|
|
|
f_out:write(dump(data))
|
|
|
|
f_out:write("\n")
|
|
|
|
f_out:close()
|
|
|
|
end
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Replaces existing settings with table.
|
|
|
|
function LuaData:reset(table)
|
|
|
|
self.data = table
|
|
|
|
self:flush()
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Writes all settings to disk (does not append).
|
|
|
|
function LuaData:flush()
|
|
|
|
if not self.file then return end
|
|
|
|
|
|
|
|
if lfs.attributes(self.file, "mode") == "file" then
|
|
|
|
for i=1, self.max_backups, 1 do
|
|
|
|
if lfs.attributes(self.file..".old."..i, "mode") == "file" then
|
2022-09-27 23:10:50 +00:00
|
|
|
logger.dbg("LuaData: Rename", self.file .. ".old." .. i, "to", self.file .. ".old." .. i+1)
|
2017-10-07 20:13:46 +00:00
|
|
|
os.rename(self.file, self.file .. ".old." .. i+1)
|
|
|
|
else
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
2022-09-27 23:10:50 +00:00
|
|
|
logger.dbg("LuaData: Rename", self.file, "to", self.file .. ".old.1")
|
2017-10-07 20:13:46 +00:00
|
|
|
os.rename(self.file, self.file .. ".old.1")
|
|
|
|
end
|
|
|
|
|
2022-09-27 23:10:50 +00:00
|
|
|
logger.dbg("LuaData: Write to", self.file)
|
2017-10-07 20:13:46 +00:00
|
|
|
local f_out = io.open(self.file, "w")
|
|
|
|
if f_out ~= nil then
|
|
|
|
f_out:write("-- we can read Lua syntax here!\n")
|
|
|
|
f_out:write(self.name.."Entry")
|
|
|
|
f_out:write(dump(self.data))
|
|
|
|
f_out:write("\n")
|
|
|
|
f_out:close()
|
|
|
|
end
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
|
|
|
|
return LuaData
|