Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ The release run heads these entries with the version and opens a fresh

## Unreleased

- **Breaking**: the codes the page reports through `odr.onError` and
`odr.onEditRefused` moved from 1 to 9 onto 1001 to 1009. `readOnly` is 1005,
not 5; the `reason` string is unchanged.

- **Added**: `odr::ErrorCode` (`odr/error_code.hpp`), one number space every
binding reports for both a thrown exception and a refused edit. `ODRError`
casts from it, `OdrException.getCode()` and the wasm envelope's `code` carry
it, and `enumTables()` and the python module expose it.

- **Breaking**: `AnchorType` gains `none` as its first value, so every later
ordinal shifts by one. `Frame::anchor_type()` answers it for a frame that
does not exist, instead of `as_char`, which a real frame also answers.
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ set(ODR_SOURCE_FILES
"src/odr/document.cpp"
"src/odr/document_element.cpp"
"src/odr/document_path.cpp"
"src/odr/error_code.cpp"
"src/odr/exceptions.cpp"
"src/odr/file.cpp"
"src/odr/filesystem.cpp"
Expand Down
6 changes: 4 additions & 2 deletions apple/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ consumer, and a SwiftPM binary target gives the consumer no way to pass
`guarded` where the caller gets an `NSError **`, `guarded_value` for a
property, and `guarded_void` for a `void` method. Pick a fallback that keeps
the caller sane β€” `YES` for a walker's `end`, so a `while (!end)` loop
terminates instead of spinning. The `NSError` code list mirrors
`jni/src/odr_jni.cpp::throw_java` β€” keep the two in step.
terminates instead of spinning. `ODRError` is the head of `odr::ErrorCode`
numbered the same, so `error_code()` is a cast and `ODRInternal.mm`
static_asserts it. Adding a case means adding the enumerator here too; a code
past the list reports `ODRErrorUnknown`.
- **Elements carry their owner.** Most public C++ handles own a `shared_ptr`,
so a wrapper holding one by value is self-sufficient and needs no keep-alive.
`odr::Element` and `odr::HtmlView` are the exceptions: the first holds a bare
Expand Down
8 changes: 4 additions & 4 deletions apple/include/OdrCoreObjC/ODRError.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ NS_ASSUME_NONNULL_BEGIN
/// Domain of every error this framework reports.
extern NSErrorDomain const ODRErrorDomain;

/// The subset of `odr::Exception` the bindings tell apart, mirroring the one
/// `jni/src/odr_jni.cpp` maps to typed java exceptions. Anything else arrives
/// as `ODRErrorUnknown`; the C++ message is always the error's
/// `NSLocalizedDescriptionKey`, so nothing is lost by not having a code.
/// The head of `odr::ErrorCode`, numbered the same β€” `ODRInternal.mm` asserts
/// that and casts. A code past this list arrives as `ODRErrorUnknown`; the C++
/// message is always the error's `NSLocalizedDescriptionKey`, so nothing is
/// lost by not having a case here.
typedef NS_ERROR_ENUM(ODRErrorDomain, ODRError){
ODRErrorUnknown = 1,
ODRErrorUnsupportedOperation = 2,
Expand Down
61 changes: 31 additions & 30 deletions apple/src/ODRInternal.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#import <OdrCoreObjC/ODRError.h>

#include <odr/error_code.hpp>

#include <odr/exceptions.hpp>

#include <exception>
Expand Down Expand Up @@ -59,39 +61,38 @@

namespace {

/// The code for the exception being handled. Mirrors the mapping in
/// `jni/src/odr_jni.cpp::throw_java` β€” keep the two in step.
#define ODR_SAME_CODE(code, objc) \
static_assert(static_cast<int>(odr::ErrorCode::code) == objc, \
"ODRError must stay odr::ErrorCode numbered the same")

ODR_SAME_CODE(unknown, ODRErrorUnknown);
ODR_SAME_CODE(unsupported_operation, ODRErrorUnsupportedOperation);
ODR_SAME_CODE(file_not_found, ODRErrorFileNotFound);
ODR_SAME_CODE(unknown_file_type, ODRErrorUnknownFileType);
ODR_SAME_CODE(unsupported_file_type, ODRErrorUnsupportedFileType);
ODR_SAME_CODE(file_read_error, ODRErrorFileReadError);
ODR_SAME_CODE(file_write_error, ODRErrorFileWriteError);
ODR_SAME_CODE(no_document_file, ODRErrorNoDocumentFile);
ODR_SAME_CODE(unknown_document_type, ODRErrorUnknownDocumentType);
ODR_SAME_CODE(unsupported_crypto_algorithm, ODRErrorUnsupportedCryptoAlgorithm);
ODR_SAME_CODE(wrong_password, ODRErrorWrongPassword);
ODR_SAME_CODE(decryption_failed, ODRErrorDecryptionFailed);
ODR_SAME_CODE(not_encrypted, ODRErrorNotEncrypted);
ODR_SAME_CODE(file_encrypted, ODRErrorFileEncrypted);
ODR_SAME_CODE(document_copy_protected, ODRErrorDocumentCopyProtected);

#undef ODR_SAME_CODE

/// `ODRError` is `odr::ErrorCode` numbered the same, so this is a cast. A code
/// past the ones `ODRError` names reports `ODRErrorUnknown`.
ODRError error_code() {
try {
throw;
} catch (const odr::UnsupportedOperation &) {
return ODRErrorUnsupportedOperation;
} catch (const odr::FileNotFound &) {
return ODRErrorFileNotFound;
} catch (const odr::UnknownFileType &) {
return ODRErrorUnknownFileType;
} catch (const odr::UnsupportedFileType &) {
return ODRErrorUnsupportedFileType;
} catch (const odr::FileReadError &) {
return ODRErrorFileReadError;
} catch (const odr::FileWriteError &) {
return ODRErrorFileWriteError;
} catch (const odr::NoDocumentFile &) {
return ODRErrorNoDocumentFile;
} catch (const odr::UnknownDocumentType &) {
return ODRErrorUnknownDocumentType;
} catch (const odr::UnsupportedCryptoAlgorithm &) {
return ODRErrorUnsupportedCryptoAlgorithm;
} catch (const odr::WrongPasswordError &) {
return ODRErrorWrongPassword;
} catch (const odr::DecryptionFailed &) {
return ODRErrorDecryptionFailed;
} catch (const odr::NotEncryptedError &) {
return ODRErrorNotEncrypted;
} catch (const odr::FileEncryptedError &) {
return ODRErrorFileEncrypted;
} catch (const odr::DocumentCopyProtectedException &) {
return ODRErrorDocumentCopyProtected;
} catch (const std::exception &e) {
const odr::ErrorCode code = odr::error_code(e);
return code > odr::ErrorCode::document_copy_protected
? ODRErrorUnknown
: static_cast<ODRError>(code);
} catch (...) {
return ODRErrorUnknown;
}
Expand Down
6 changes: 4 additions & 2 deletions docs/design/editing.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,10 @@ project, and hand-rolling gives full control over the model↔op mapping.
runs or paragraphs. It owns:

- the **mode** β€” `enable()`, `disable()`, `isEnabled()`, `isEditable()`;
- the **refusals** β€” the code table, the repeat suppression, the outline a
refused element gets, and `odr.onEditRefused`;
- the **refusals** β€” the repeat suppression, the outline a refused element
gets, and `odr.onEditRefused`. The codes are `odr::ErrorCode` and the
renderer writes them into the page as `odr.errorCodes`, so the script holds
the wording and not the numbers;
- the **log** β€” `getOperations()`, `undo()`, `redo()`, `committed()`, and the
`dirty` / `canUndo` / `canRedo` state `odr.onEditChange` reports;
- the **keyboard classes** the page may take (decision 12).
Expand Down
16 changes: 11 additions & 5 deletions docs/design/spreadsheet-editing.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,17 @@ is the same thing spelled for a reader of the log. We still ship an English
`odr.onError` default does exactly this) and a desktop host with no catalogue
can show it as it stands.

**Codes are appended, never renumbered**, and share one space with
`odr.onError`'s β€” `errorIllegalEditNewLine` holds 1. The rule the wasm enum
ordinals already live under: appending stays silent, reordering goes loud.
Pin them in `test/browser/sheet` the way `tests/enums.test.mjs` pins the
enums.
**The codes are `odr::ErrorCode`**, defined in `src/odr/error_code.hpp` and
written into the page by `html/frontend.cpp::write_error_codes`, so the scripts
restate no number. They share one space with `odr.onError`'s and with the code
every binding reports for a thrown `odr::Exception`: below 1000 an exception
names itself, and the refusals sit from 1001, `newLine` first.

**Codes are appended, never renumbered.** The rule the wasm enum ordinals
already live under: appending stays silent, reordering goes loud. `odr_test`
pins both bands (`error_code_test.cpp`), `tests/enums.test.mjs` pins them on the
JS side, and `test/browser/sheet` reads the table `serve.py` builds from the
header rather than a copy.

**One object argument, never positional.** `onError(code, message)` cannot
grow a field without breaking every host that implements it; an object can.
Expand Down
7 changes: 5 additions & 2 deletions jni/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,11 @@ package `app.opendocument.core`. Mirrors the surface of the python bindings
enum declaration; `-1` encodes an absent `std::optional`.
- **Strings**: use `odr_jni::to_string`/`to_jstring` (real UTF-8 ↔ UTF-16),
never JNI's modified-UTF-8 `GetStringUTFChars`.
- **Exceptions**: every native body runs inside `odr_jni::guarded`; C++
exceptions map to `OdrException` subclasses (`odr_jni.cpp::throw_java`).
- **Exceptions**: every native body runs inside `odr_jni::guarded`.
`throw_java` names the `OdrException` subclass after `odr::ErrorCode`, so a
code with no class here arrives as the base and the mapping cannot drift.
Every one carries `getCode()`, the number the page and the other bindings
report too.
- Mirror the C++ names. `Logger` is bound as a `NativeResource`; entry points
that take one get an overload (e.g. `Odr.open(path, logger)`).
- `ILogger` is implementable in Java. `jni_logger.cpp`'s `JavaLogger` holds a
Expand Down
80 changes: 77 additions & 3 deletions jni/java/app/opendocument/core/OdrException.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
package app.opendocument.core;

/**
* Base class for exceptions thrown by the native library. The subclasses
* mirror the typed exceptions in {@code odr/exceptions.hpp}; native errors
* without a dedicated subclass are thrown as plain {@code OdrException}.
* Base class for exceptions thrown by the native library. The subclasses are
* named after {@code odr::ErrorCode}; a code with no subclass here arrives as
* plain {@code OdrException}.
*/
public class OdrException extends RuntimeException {
private static final long serialVersionUID = 1L;

/** {@code odr::ErrorCode::unknown}. */
private static final int UNKNOWN = 1;

private final int code;

public OdrException(String message) {
this(message, UNKNOWN);
}

public OdrException(String message, int code) {
super(message);
this.code = code;
}

/**
* The {@code odr::ErrorCode}, the same number the rendered page reports
* through {@code odr.onError} and {@code odr.onEditRefused}.
*/
public int getCode() {
return code;
}

public static final class UnsupportedOperation extends OdrException {
Expand All @@ -18,6 +36,10 @@ public static final class UnsupportedOperation extends OdrException {
public UnsupportedOperation(String message) {
super(message);
}

public UnsupportedOperation(String message, int code) {
super(message, code);
}
}

public static final class FileNotFound extends OdrException {
Expand All @@ -26,6 +48,10 @@ public static final class FileNotFound extends OdrException {
public FileNotFound(String message) {
super(message);
}

public FileNotFound(String message, int code) {
super(message, code);
}
}

public static final class UnknownFileType extends OdrException {
Expand All @@ -34,6 +60,10 @@ public static final class UnknownFileType extends OdrException {
public UnknownFileType(String message) {
super(message);
}

public UnknownFileType(String message, int code) {
super(message, code);
}
}

public static final class UnsupportedFileType extends OdrException {
Expand All @@ -42,6 +72,10 @@ public static final class UnsupportedFileType extends OdrException {
public UnsupportedFileType(String message) {
super(message);
}

public UnsupportedFileType(String message, int code) {
super(message, code);
}
}

public static final class FileReadError extends OdrException {
Expand All @@ -50,6 +84,10 @@ public static final class FileReadError extends OdrException {
public FileReadError(String message) {
super(message);
}

public FileReadError(String message, int code) {
super(message, code);
}
}

public static final class FileWriteError extends OdrException {
Expand All @@ -58,6 +96,10 @@ public static final class FileWriteError extends OdrException {
public FileWriteError(String message) {
super(message);
}

public FileWriteError(String message, int code) {
super(message, code);
}
}

public static final class NoDocumentFile extends OdrException {
Expand All @@ -66,6 +108,10 @@ public static final class NoDocumentFile extends OdrException {
public NoDocumentFile(String message) {
super(message);
}

public NoDocumentFile(String message, int code) {
super(message, code);
}
}

public static final class UnknownDocumentType extends OdrException {
Expand All @@ -74,6 +120,10 @@ public static final class UnknownDocumentType extends OdrException {
public UnknownDocumentType(String message) {
super(message);
}

public UnknownDocumentType(String message, int code) {
super(message, code);
}
}

public static final class UnsupportedCryptoAlgorithm extends OdrException {
Expand All @@ -82,6 +132,10 @@ public static final class UnsupportedCryptoAlgorithm extends OdrException {
public UnsupportedCryptoAlgorithm(String message) {
super(message);
}

public UnsupportedCryptoAlgorithm(String message, int code) {
super(message, code);
}
}

public static final class WrongPassword extends OdrException {
Expand All @@ -90,6 +144,10 @@ public static final class WrongPassword extends OdrException {
public WrongPassword(String message) {
super(message);
}

public WrongPassword(String message, int code) {
super(message, code);
}
}

public static final class DecryptionFailed extends OdrException {
Expand All @@ -98,6 +156,10 @@ public static final class DecryptionFailed extends OdrException {
public DecryptionFailed(String message) {
super(message);
}

public DecryptionFailed(String message, int code) {
super(message, code);
}
}

public static final class NotEncrypted extends OdrException {
Expand All @@ -106,6 +168,10 @@ public static final class NotEncrypted extends OdrException {
public NotEncrypted(String message) {
super(message);
}

public NotEncrypted(String message, int code) {
super(message, code);
}
}

public static final class FileEncrypted extends OdrException {
Expand All @@ -114,6 +180,10 @@ public static final class FileEncrypted extends OdrException {
public FileEncrypted(String message) {
super(message);
}

public FileEncrypted(String message, int code) {
super(message, code);
}
}

public static final class DocumentCopyProtected extends OdrException {
Expand All @@ -122,5 +192,9 @@ public static final class DocumentCopyProtected extends OdrException {
public DocumentCopyProtected(String message) {
super(message);
}

public DocumentCopyProtected(String message, int code) {
super(message, code);
}
}
}
Loading
Loading