{"id":17252088,"url":"https://github.com/ppetr/jlens","last_synced_at":"2025-07-28T08:06:35.405Z","repository":{"id":5338122,"uuid":"6523679","full_name":"ppetr/jLens","owner":"ppetr","description":"Auto-generated lenses for Java beans.","archived":false,"fork":false,"pushed_at":"2012-12-15T19:58:27.000Z","size":195,"stargazers_count":14,"open_issues_count":0,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-14T05:39:38.615Z","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":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ppetr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-11-03T20:09:17.000Z","updated_at":"2023-08-22T03:10:38.000Z","dependencies_parsed_at":"2022-08-06T18:01:34.786Z","dependency_job_id":null,"html_url":"https://github.com/ppetr/jLens","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/ppetr/jLens","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ppetr%2FjLens","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ppetr%2FjLens/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ppetr%2FjLens/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ppetr%2FjLens/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ppetr","download_url":"https://codeload.github.com/ppetr/jLens/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ppetr%2FjLens/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267482004,"owners_count":24094508,"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","status":"online","status_checked_at":"2025-07-28T02:00:09.689Z","response_time":68,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":"2024-10-15T06:52:59.949Z","updated_at":"2025-07-28T08:06:35.359Z","avatar_url":"https://github.com/ppetr.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# jLens\n\nAuto-generated [lenses](http://stackoverflow.com/q/8307370/1333025) for Java beans.\n\nRequirements: Java 1.6 or later.\n\n\n## What are lenses?\n\n`Lens\u003cR,F\u003e` is an object that knows how to retrieve or update something of type\n`F` inside objects of type `R`.\n(See also [this SO question](http://stackoverflow.com/q/8307370/1333025).)\n\nEach `Lens` has two operations:\n\n```java\npublic void set(R record, F fieldValue);\npublic F get(R target);\n```\n\nThis is similar to standard Java's getters and setters, but:\n\n- Getters/setters are methods of a bean. Lenses are separate objects that\n  get the bean to work on as an argument.\n- Because they're separate objects, we can operate on them. For example, we can\n  pass them as arguments to methods.\n- Similarly to functions, lenses can be composed. If we have `Lens\u003cR,F\u003e` and\n  `Lens\u003cF,U\u003e` we can compose them to get `Lens\u003cR,U\u003e`.\n\n\n## What does jLens do?\n\nApart from defining the core classes and interfaces (such as `Lens`), jLens\ncontains an annotation processor that automatically generates lenses from bean\nsetters/getters during compilation. All we need is to annotate our bean class\nwith `@LensProperties`. For example if we have class\n\n```java\n@LensProperties\npublic class Person {\n    private String name;\n    private Candidate seller;\n\n    public String getName()       { return this.name; }\n    public void setName(String v) { this.name = v; }\n\n    public Candidate getSeller()        { return this.seller; }\n    public void setSeller(Candidate v)  { this.seller = v; }\n}\n\npublic class Candidate {\n  ...\n}\n```\n\nthe processor will generate a helper class with static fields, something like:\n\n```java\npublic class Person_L {\n    public static final AbstractLens\u003cPerson,String\u003e name = ...;\n    public static final AbstractLens\u003cPerson,Candidate\u003e seller = ...;\n}\n```\n\nBecause `Candidate` isn't annotated, nothing is generated for it.\n\n\n### Composition ###\n\nWhat if we want to get a person's seller's name? Or person's seller's seller's\nname? We can type\n\n```java\nLenses.join(Person_L.seller, Person_L.name)\n// or\nLenses.join(Person_L.seller, Lenses.join(Person_L.seller, Person_L.name))\n```\n\nHowever this is not very convenient. Therefore jLens generates helper methods\nto all generated lenses. For attribute `someAttr` it generates method\n`someAttr()` that appends the appropriate lens to the current one. So in this\nexample, we can write just\n\n```java\nPerson_L.seller.name()\n// or\nPerson_L.seller.seller().name()\n```\n\nSee the examples package for a complete example that generates the lenses and\ntests the generated lenses.\n\n## What are lenses useful for?\n\n### An example\n\nSuppose we're creating a GUI that allows the user to edit some data. \nWe want a component that pops up a dialog window where the user can update\na `Person'`s name. Without lenses, we'd have to hard-wire `Person` and its\n`getName()` and `setName(...)` into the component. This would be quite\ninconvenient, and we'd end up with a separate dialog class for every field we\nwanted to edit. \n\nWith lenses, we can create a single generic editor dialog that can edit\nanything that has a `String` inside:\n\n```java\npublic EditorDialog\u003cM\u003e extends ... {\n    protected final M model;\n    protected final Lens\u003cM,String\u003e lens;\n\n    public EditorDialog(M model, Lens\u003cM,String\u003e lens) {\n        this.model = model;\n        this.lens = lens;\n    }\n\n    protected String load() {\n        return lens.get(model);\n    }\n    protected void save(String value) {\n        lens.set(model, value);\n    }\n    ...\n}\n```\n\nNow whenever `EditorDialog` needs to read the value that should be filled into\nits text field, it simply calls `load()`. And when the user edits the\nfield and closes the dialog, it simply saves the value by calling\n`save(newValue)`. Voilà, we have a generic editor component that\nallows us to edit a String value within anything we want. We can use it to edit\na person's name by calling `new EditorDialog(somePerson, Person_L.name)`. But\nwe can use it as well to edit a person's seller's name by calling  `new\nEditorDialog(somePerson, Person_L.seller.name())` etc.\n\n#### Simplification using `Store`\n\nIn the previous example, we didn't actually need the model except to pass it to the lens. This is a common situation so it's worth simplifying it using some general solution: we define interface `Store`, which represents something from which we can read a value of type `F`, or update the value. Then, given a lens and its record object, we can combine them to create a `Store`. (And of course we can create custom `Store`s that aren't composed of a lens and a record.)\n\n```java\npublic EditorDialog1 extends ... {\n    protected final Store\u003cString\u003e store;\n\n    public EditorDialog(Store\u003cString\u003e store) {\n        this.store = store;\n    }\n    public \u003cM\u003e EditorDialog(M model, Lens\u003cM,String\u003e lens) {\n        this(Lenses.store(lens, model));\n    }\n\n    protected String load() {\n        return store.get();\n    }\n    protected void save(String value) {\n        store.set(value);\n    }\n    ...\n}\n```\n\n## Things to be done\n\n1. Generate javadoc documentation and publish it here on github (and automate the process).\n1. Publish the library in a public maven repository, preferably some standard one.\n\nIf you want to help with any of this, you'll be most welcome.\n\n# Copyright\n\nCopyright 2012, Petr Pudlák\n\nContact: [petr.pudlak.name](http://petr.pudlak.name/).\n\n![LGPLv3](https://www.gnu.org/graphics/lgplv3-88x31.png)\n\nThis program is free software: you can redistribute it and/or modify it under\nthe terms of the GNU Lesser General Public License as published by the Free\nSoftware Foundation, either version 3 of the License, or (at your option) any\nlater version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY\nWARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A\nPARTICULAR PURPOSE.  See the GNU Lesser General Public License for more\ndetails.\n\nYou should have received a copy of the GNU Lesser General Public License along\nwith this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fppetr%2Fjlens","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fppetr%2Fjlens","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fppetr%2Fjlens/lists"}