{"id":28433451,"url":"https://github.com/kohlschutter/stringhold","last_synced_at":"2025-06-30T04:31:37.429Z","repository":{"id":87679681,"uuid":"569018104","full_name":"kohlschutter/stringhold","owner":"kohlschutter","description":"Java library to reduce impact of constructing large strings from hierarchies, different sources, etc.","archived":false,"fork":false,"pushed_at":"2025-04-06T22:11:05.000Z","size":2726,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-05T18:09:35.524Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kohlschutter.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-21T22:45:16.000Z","updated_at":"2025-04-06T22:11:09.000Z","dependencies_parsed_at":"2023-12-10T22:26:56.821Z","dependency_job_id":"42133dfd-1157-4ce9-a744-6392c89de0c2","html_url":"https://github.com/kohlschutter/stringhold","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/kohlschutter/stringhold","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kohlschutter%2Fstringhold","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kohlschutter%2Fstringhold/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kohlschutter%2Fstringhold/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kohlschutter%2Fstringhold/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kohlschutter","download_url":"https://codeload.github.com/kohlschutter/stringhold/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kohlschutter%2Fstringhold/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262710147,"owners_count":23351943,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-05T18:09:34.671Z","updated_at":"2025-06-30T04:31:37.417Z","avatar_url":"https://github.com/kohlschutter.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"### stringhold: A java.lang.String holder\n\n# stringhold\n\nConcatenating large Strings in Java, potentially with deep hierarchies, with inputs from different sources (local or network), etc., can be unnecessarily complex:\n\n- Even with StringBuilders, text gets copied around multiple times\n- Character buffers get reallocated over and over\n- A missing input fragment blocks the assembly of the full output\n- The output is written to a stream, so why did we assemble it to String in the first place?\n- The output is being assembled and eventually we realize it's too long\n- We don't realize we ever only need the first line of the 1 megabyte output, but we still create the full thing, involving complex computations\n- and so on\n\n**stringhold** comes to the rescue.\n\nStringhold provides classes replacing or improving the functionality of the following typical Java classes:\n\n- String\n- CharSequence\n- Reader\n- Appendable\n- StringBuilder\n- StringBuffer\n- Writer\n\n## StringHolder API\n\nA `StringHolder` is something *holding* data that may turn into a String. This can simply be a String *itself*, something that can *assemble* a String from its own state, or something that gets a String *supplied* from somewhere else (e.g., a `Supplier\u003cString\u003e`).\n\nThe `StringHolder` is a `CharSequence`, and calling `#toString()` will yield the string it holds. All other methods, like `#length()`, `#charAt`, `#subSequence` etc. work the way you expect it. In addition, you can all `#toReader()` to wrap the contents as a `java.io.Reader`, and you can call `#appendTo(Appendable)` to write the contents to some other `Appendable`.\n\nIf you want to concatenate several `StringHolders`, very much like `StringBuilder`, you can use a `StringHolderSequence`, which by itself also is an `Appendable`, so you can also append Strings directly.\n\nYou can use a `Reader` source as a supplying source through the `ReaderStringHolder` subclass.\n\nYou can also build your own StringHolder subclass, if you want to.\n\n## The Twist\n\nAll the operations listed above may in fact never create a String or concatenate them. If all that you need is to copy data from a Reader to a Writer, there is no need to construct a single String from it!\n\n**stringhold** takes great care of delaying the materialization of String instances from input data as much as possible.\n\n## Relaxing assumptions\n\nOften, a criterion such as \"too long\" doesn't require to know the exact length of something. It's enough that we know we've hit a certain minimum length.\n\nAlso, when estimating buffer sizes, a minimum is handy, but so is an *expected length*, even though that may not be as clearly defined as a minimum.\n\nLastly, sometimes we know the exact length but not the content.\n\n`StringHolder` instances can use this information for typical operations such as:\n\n- `#equals(Object)` checks (if a minimum length of the other object exceeds our own length, it cannot be a match).\n- `#isEmpty()` checks (a minimum length of 1 is sufficient to rule that out)\n\n## Exceptions and error conditions\n\n**stringhold** can handle situations where the Java API does not expect an `IOException` to be thrown, but it may still arise from within some code, for example, when assembling a String from a `Supplier` or `Reader` for use in `toString()` or when appending somewhere else.\n\nThe exception can either be wrapped in an `IllegalStateException` or the output can be augmented (e.g., just flushed, replaced with an empty string if possible, or appending the error message or the full stack trace for debugging).\n\nIn addition, `StringHolder` supports a `checkError()` method to indicate that some constraints were violated.\n\n## StringHolderScope\n\nA set of StringHolders can be associated with a `StringHolderScope`, which receives callbacks upon events regarding these `StringHolder` instances. Such scopes can be used to implement quota checks and error listeners, for example.\n\n## Modern Java\n\n**stringhold** is fully compatible with Java 8 and above. Thanks to a Multirelease jar structure, it can optimize the use of Java methods introduced in newer Java releases whenever possible, such as `CharSequence#isEmpty()` — without complicating its own public API.\n\n## Code Quality\n\nThis project currently maintains a [100% code coverage](https://kohlschutter.github.io/stringhold/stringhold-codecoverage/jacoco-aggregate/index.html) policy.\n\nThis is one of the projects where this makes actual sense.\n\nThe **stringhold** API is expected to not have breaking changes across minor and patch releases. Changes in behavior are possible but should be clearly marked in the changelog.\n\n# Usage Examples\n\n## The Basics\n\nA StringHolder using a String directly:\n\n\tStringHolder h = StringHolder.withContent(\"Some string\");\n\th.isString(); // always true\n\n\th.toString(); // simply returns the associated string.\n\nA StringHolder using a String supplier:\n\n\tStringHolder h = StringHolder.withSupplier(() -\u003e \"Some string\");\n\th.isString(); // not true yet\n\n\th.toString(); // calls Supplier.get()\n\th.isString(); // now true\n\n\th.toString(); // doesn't Supplier.get() a second time, string is cached\n\nA StringHolder using a String supplier and some length constraints:\n\n\tStringHolder h = StringHolder.withSupplierMinimumAndExpectedLength(5, 20, () -\u003e \"Some string\");\n\th.isEmpty(); // false because we claim the string is at least 5 characters long\n\th.isString(); // not true yet\n\nA StringHolder that reads contents from a function that supplies StringReader instances:\n\n\tStringHolder h = StringHolder.withReaderSupplier(() -\u003e new StringReader(\"hello\"), (\n        e) -\u003e IOExceptionHandler.ExceptionResponse.ILLEGAL_STATE);\n\nA sequence of String(Holder)s, some are nested:\n\n\tStringHolderSequence seq = StringHolder.newSequence();\n\tseq.append(\"Hello\");\n\tseq.append(' ');\n\tseq.append(StringHolder.newSequence().append(StringHolder.withSupplier(() -\u003e \"World\"));\n\n\t// Append to a writer\n\ttry (Writer out = new FileWriter(new File(\"/tmp/out\"))) {\n\t\tseq.appendTo(out);\n\t}\n\n\tseq.toString(); // \"Hello World\"\n\nA sequence of String(Holder)s that can be assembled/appended out of order via scatter-gatter (speed up overall assembly time through parallelization):\n\n\tStringHolderSequence seq = StringHolder.newAsyncSequence();\n\tseq.append(veryComplexStringHolder);\n\tseq.append(anotherStringHolder);\n\t\n\t// seq.toString() / seq.appendTo() may internally serialize the second StringHolder first.\n\t// The final order is still guaranteed to be correct.\n\t// This may save time at the cost of constructing temporary StringBuilders.\n\nYou can also define a conditional StringHolder, which may be defined but later excluded if a condition is not met:\n\n    StringHolder seq = StringHolder.withContent(\"Hello\", //\n\t   StringHolder.withConditionalStringHolder(StringHolder.withContent(\" World\"), (o) -\u003e {\n \t     return checkIfIncluded(o); // false: exclude; true: include\n\t   }));\n\tseq.toString() // returns \"Hello\" or \"Hello World\", depending on checkIfIncluded\n\n\n## The full API\n\n* [API JavaDoc](https://kohlschutter.github.io/stringhold/stringhold-common/apidocs/index.html)\n* [Source code](https://kohlschutter.github.io/stringhold/stringhold-common/xref/index.html)\n* [Test code](https://kohlschutter.github.io/stringhold/stringhold-common/xref-test/index.html)\n* [Code coverage](https://kohlschutter.github.io/stringhold/stringhold-codecoverage/jacoco-aggregate/index.html)\n* [Project website](https://kohlschutter.github.io/stringhold/)\n\n## Benchmarks\n\nTBD\n\n## Future Improvements\n\n- Add optimizations for the `IntStream` API\n\n## Frequently Asked Questions\n\nQ: Is this an accidental implementation of half of Lisp?\n\nA: Maybe.\n\n## Installation\n\n### Maven\n\nAdd the following dependency:\n\n    \u003cdependency\u003e\n        \u003cgroupId\u003ecom.kohlschutter.stringhold\u003c/groupId\u003e\n        \u003cartifactId\u003estringhold-common\u003c/artifactId\u003e\n        \u003cversion\u003e1.0.0\u003c/version\u003e\n    \u003c/dependency\u003e\n\n### Gradle\n\n    dependencies {\n        implementation 'com.kohlschutter.stringhold:stringhold-common:1.0.0'\n    }\n\n## Building from source\n\nYou currently need Maven or Eclipse with m2e, and Java 15 or later. Just run:\n\n\tmvn clean install\n\nTo reformat code, which simplifies pull requests and restores general sanity, use:\n\n\tmvn process-sources -Preformat\n\n# Changelog\n\n### _(2025-04-06)_ **stringhold 1.0.3*\n\n - Code cleanup\n\n### _(2024-03-09)_ **stringhold 1.0.2*\n\n - Add indexOf\n\n### _(2023-12-11)_ **stringhold 1.0.1*\n\n - Fix liqp dependency\n\n### _(2023-12-10)_ **stringhold 1.0.0**\n\n - Initial release\n\n# Legal Notices\n\nCopyright 2022-2024 Christian Kohlschuetter \u003cchristian@kohlschutter.com\u003e\n\nSPDX-License-Identifier: Apache-2.0\nSee NOTICE and LICENSE for license details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkohlschutter%2Fstringhold","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkohlschutter%2Fstringhold","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkohlschutter%2Fstringhold/lists"}