├── .gitignore ├── src └── dsui │ ├── ui.clj │ ├── core.clj │ ├── examples.clj │ └── swing.clj ├── img ├── data_DSUI.png └── data_cider.png ├── deps.edn ├── test └── dsui │ ├── swing_test.clj │ └── core_test.clj ├── project.clj ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .nrepl-port -------------------------------------------------------------------------------- /src/dsui/ui.clj: -------------------------------------------------------------------------------- 1 | (ns dsui.ui) 2 | 3 | (def colnumber 3) 4 | -------------------------------------------------------------------------------- /img/data_DSUI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azel4231/dsui/HEAD/img/data_DSUI.png -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src"] 2 | :deps {org.clojure/clojure {:mvn/version "1.9.0"}}} 3 | -------------------------------------------------------------------------------- /img/data_cider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azel4231/dsui/HEAD/img/data_cider.png -------------------------------------------------------------------------------- /test/dsui/swing_test.clj: -------------------------------------------------------------------------------- 1 | (ns dsui.swing-test 2 | (:use [clojure.test]) 3 | (:require [dsui.swing :as swing])) 4 | 5 | (deftest table-model-test 6 | (let [tm (swing/table-model '("A" "B" "c") [[1 2 3] 7 | [:1 :2 :3]])] 8 | (is (= 3 (.getColumnCount tm))) 9 | (is (= 2 (.getRowCount tm))))) 10 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject org.clojars.azel4231/dsui "0.1.1" 2 | :description "Generate a readonly form-based UI for arbitrary data" 3 | :url "https://github.com/Azel4231/dsui" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | 7 | :dependencies [[org.clojure/clojure "1.9.0"] 8 | [org.clojure/test.check "0.9.0" :scope "test"]] 9 | 10 | :deploy-repositories [["clojars" {:sign-releases false 11 | :url "https://clojars.org/repo"}]] 12 | 13 | :plugins [] 14 | :main dsui.examples 15 | :source-paths ["src"] 16 | :test-paths ["test"] 17 | :target-path "target/%s" 18 | :profiles {:uberjar {:aot :all}}) 19 | -------------------------------------------------------------------------------- /src/dsui/core.clj: -------------------------------------------------------------------------------- 1 | (ns dsui.core 2 | (:require [clojure.spec.alpha :as s])) 3 | 4 | (set! *warn-on-reflection* true) 5 | 6 | (defn scalar? [v] 7 | ((complement coll?) v)) 8 | 9 | (defn same? [f coll] 10 | (if (empty? coll) false 11 | (apply = (map f coll)))) 12 | 13 | (defn homogeneous? [ms] 14 | (same? keys ms)) 15 | 16 | (defn scalar-map? [m] 17 | (every? scalar? (vals m))) 18 | 19 | (s/def ::ds (s/or ::entity ::entity 20 | ::table-of-entities ::table-of-entities 21 | ::matrix ::matrix 22 | ::labeled-ds ::labeled-ds 23 | ::list-of-scalars ::list-of-scalars 24 | ::list-of-dss ::list-of-dss)) 25 | 26 | (s/def ::table-of-entities (s/and (s/coll-of map? :min-count 2) 27 | (s/every scalar-map?) 28 | homogeneous?)) 29 | 30 | (s/def ::matrix (s/and (s/coll-of ::list-of-scalars) 31 | #(same? count %))) 32 | 33 | (s/def ::list-of-scalars (s/coll-of ::scalar)) 34 | 35 | (s/def ::list-of-dss (s/and (s/coll-of ::ds-or-scalar) 36 | (complement map?))) 37 | 38 | (s/def ::entity (s/map-of any? ::ds-or-scalar)) 39 | 40 | (s/def ::labeled-ds (s/and map-entry? 41 | (s/tuple keyword? ::ds-or-scalar))) 42 | 43 | (s/def ::ds-or-scalar (s/or ::nested-ds ::ds 44 | ::scalar ::scalar)) 45 | 46 | (s/def ::scalar scalar?) 47 | 48 | -------------------------------------------------------------------------------- /test/dsui/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns dsui.core-test 2 | (:use dsui.core) 3 | [:use clojure.test] 4 | (:require [clojure.spec.alpha :as s] 5 | [dsui.core :as core] 6 | [dsui.swing :as swing])) 7 | 8 | (deftest scalar-test 9 | (is (scalar? 1)) 10 | (is (not (scalar? [])))) 11 | 12 | (def scalarmap {:a 1 :b "2" :c 1/2}) 13 | (def nestmap {:a []}) 14 | (deftest scalar-map-tets 15 | (is (scalar-map? scalarmap)) 16 | (is (not (scalar-map? nestmap)))) 17 | 18 | (def homo '({:a 1 :b 2} {:a "1" :b "2"} {:a 1/3 :b 3/7})) 19 | (def inhomo [{:a 1} {:b 2}]) 20 | (def inhomo2 [{:a 1} {:a 2 :b 3}]) 21 | (deftest homo-test 22 | (is (homogeneous? homo)) 23 | (is (not (homogeneous? inhomo))) 24 | (is (not (homogeneous? inhomo2)))) 25 | 26 | 27 | 28 | (def list-data ["a" "B" 1 2 3]) 29 | (deftest conform_conformed_data 30 | (let [conformed-data (s/conform :dsui.core/ds list-data) 31 | conformed²-data (s/conform :dsui.core/ds conformed-data)] 32 | (is (= conformed-data 33 | [:dsui.core/list-of-scalars ["a" "B" 1 2 3]])) 34 | (is (map-entry? conformed-data)) 35 | (is (= conformed²-data 36 | [:dsui.core/labeled-ds [:dsui.core/list-of-scalars 37 | [:dsui.core/nested-ds 38 | [:dsui.core/list-of-scalars ["a" "B" 1 2 3]]]]])))) 39 | 40 | (deftest matrix-test 41 | (is (= (s/conform :dsui.core/ds [["a" "B"] [1 2]]) 42 | [:dsui.core/matrix [["a" "B"] [1 2]]])) 43 | ;; test same structure, only with map entries 44 | (is (= (s/conform :dsui.core/ds (seq {"a" "B" 1 2})) 45 | [:dsui.core/matrix [["a" "B"] [1 2]]]))) 46 | 47 | (deftest list-of-scalars-test 48 | (is (= (s/conform :dsui.core/ds ["a" "B"]) 49 | [:dsui.core/list-of-scalars ["a" "B"]]))) 50 | 51 | (deftest scalar-entity-test 52 | (is (= (s/conform :dsui.core/ds {:a "A" 53 | :b 2 54 | :c 1/3 55 | :d 4.0}) 56 | [:dsui.core/entity {:a [:dsui.core/scalar "A"] 57 | :b [:dsui.core/scalar 2] 58 | :c [:dsui.core/scalar 1/3] 59 | :d [:dsui.core/scalar 4.0]}]))) 60 | 61 | ;; match table if there is more than one entity, otherwise detect entity in a list 62 | (deftest table-test 63 | (is (= [:dsui.core/table-of-entities [{:a 1} 64 | {:a 2}]] 65 | (s/conform :dsui.core/ds [{:a 1} 66 | {:a 2}]))) 67 | (is (= [:dsui.core/list-of-dss 68 | [[:dsui.core/nested-ds 69 | [:dsui.core/entity {:a [:dsui.core/scalar 1]}]]]] 70 | (s/conform :dsui.core/ds [{:a 1}])))) 71 | 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DSUI 2 | A Clojure tool for displaying arbitrary, nested data structures as a read-only, form-based UI. DSUI stands for "Data Structure User Interface". 3 | 4 | [![Clojars Project](https://img.shields.io/clojars/v/org.clojars.azel4231/dsui.svg)](https://clojars.org/org.clojars.azel4231/dsui) 5 | 6 | ## Motivation 7 | 8 | 1. Being tired of writing UI code. 9 | 10 | 1. Avoiding this: 11 | ![Data in Cider](img/data_cider.png) 12 | 13 | 1. And getting this instead: 14 | ![Data in DSUI](img/data_DSUI.png) 15 | 16 | ## Usage 17 | 18 | ### Installing the library 19 | 20 | Via deps: 21 | 22 | ```clojure 23 | {:deps {org.clojars.azel4231/dsui {:mvn/version "0.1.1"}}} 24 | ``` 25 | 26 | Via lein: 27 | 28 | ```clojure 29 | :dependencies [[org.clojars.azel4231/dsui "0.1.1"]] 30 | ``` 31 | 32 | Via maven: 33 | 34 | ``` 35 | 36 | org.clojars.azel4231 37 | dsui 38 | 0.1.1 39 | 40 | ``` 41 | 42 | ### Calling the library 43 | 44 | ```clojure 45 | (require '[dsui.swing :as d]) 46 | ``` 47 | 48 | Display data as a form-based UI: 49 | 50 | ```clojure 51 | (d/dsui any-data) 52 | ``` 53 | 54 | Show how data conforms to a spec. Useful for specs with choices (via s/or) or labeled values (vis s/cat). If the data does not conform, displays the explanation (explain-data) as a ui: 55 | 56 | ```clojure 57 | (d/conform-ui ::any-spec any-data) 58 | ``` 59 | 60 | Attach dsui to a ref and leave it open: 61 | 62 | ```clojure 63 | (defonce state (atom [])) 64 | (defonce ui (d/watch-ui state)) 65 | ``` 66 | 67 | Swap new data into the atom to update the watch-ui window (REPL workflow) 68 | 69 | ```clojure 70 | (reset! state '[1 2 3 "A" "B" "C" + - * /]) 71 | ``` 72 | 73 | Or just have it display your app's changing app-state. 74 | 75 | 76 | ## Basic Features 77 | Displays: 78 | - maps as forms, nested data structures a tabbed panes 79 | - sequential data (including vectors, lists and sets) as vertical tabbed panes with (0..n) as a title 80 | - lists containing only scalar values as JLists 81 | - Matrices (vector of vectors) as tables 82 | - Lists of maps, that have the same keyset and contain scalar values only, as tables 83 | - conformed data (labeled data) returned by specs as vertical tabbed panes with the label as a title. Thus DSUI can be also used to see how things conformed when writing specs 84 | 85 | Does not support: 86 | - a single scalar value 87 | 88 | Not suited for: 89 | - graph-like data 90 | - large data-sets (generating the UI is currently eager) 91 | 92 | ## How it works 93 | DSUI uses clojure.spec to "parse" an arbitrary data structure. The conformed data is used to generate the swing UI by calling a multimethod that polymorphically creates different types of UI elements. 94 | 95 | More detailed explanations can be found here and here 96 | 97 | ## Next Steps 98 | - Decomplect processing pipeline: move parts that are agnostic of the UI framework into a separate step so they can be reused for other UI frameworks. 99 | - Migrate to fn-fx 100 | 101 | ## Distant Future 102 | - Make UI editable (e.g. swap value to an atom) 103 | 104 | ## License 105 | Distributed under the Eclipse Public License, the same as Clojure. 106 | -------------------------------------------------------------------------------- /src/dsui/examples.clj: -------------------------------------------------------------------------------- 1 | (ns dsui.examples 2 | (:require [clojure.spec.alpha :as s] 3 | [dsui.swing :as swing])) 4 | 5 | 6 | (def uni {:name "Foo-University of Bar" 7 | :students [{:name "John Doe" :student-id "12345"} 8 | {:name "Jane Doe" :student-id "11111"} 9 | {:name "Dr. Who" :student-id "?"}] 10 | :courses [{:name "Linear Algebra" :max-students 15 :room "Gauss" :registered ["11111" "?"]} 11 | {:name "Introduction to Algorithms" :max-students 25 :room "Dijkstra" :registered ["12345" "?"]}]}) 12 | 13 | (def tbl [{:surname "Jane" :lastname "Doe" :occupation "unknown"} 14 | {:surname "Jimmy" :lastname "McNulty" :occupation "Criminal Investigator"} 15 | {:surname "Bunk" :lastname "Moreland" :occupation "Criminal Investigator"} 16 | {:surname "Omar" :lastname "Little" :occupation "?"} 17 | ]) 18 | 19 | 20 | (def mage 21 | {:id (. java.util.UUID randomUUID) 22 | :name "John Doe" 23 | :player "" 24 | :concept "" 25 | :virtue "" 26 | :vice "" 27 | :size-natural 5 28 | :properties {:path :obrimos 29 | :order :mysterium 30 | :cabal "" 31 | :gnosis 1 32 | :willpower 1 33 | :wisdom 7 34 | :merits #{} 35 | :flaws #{} 36 | :attributes {:strength 1 :dexterity 5 :stamina 1, 37 | :intelligence 1 :wits 1 :resolve 1, 38 | :presence 1 :manipulation 1 :composure 1} 39 | :skills {:academics 0 :computer 0 :crafts 0 :investigation 0 :medicine 0 :occult 0 :politics 0 :science 0, 40 | :athletics 0 :brawl 0 :drive 0 :firearms 0 :larceny 0 :stealth 0 :survival 0 :weaponry 0, 41 | :animalKen 0 :empathy 0 :expression 0 :intimidation 0 :persuasion 0 :socialize 0 :streetwise 0 :subterfuge 0} 42 | :arcana {:death 0 43 | :fate 0 44 | :forces 0 45 | :life 0 46 | :matter 0 47 | :mind 0 48 | :prime 0 49 | :space 0 50 | :spirit 0 51 | :time 0} 52 | :rotes [] 53 | :weapons [] 54 | :experience-total 0 55 | :experience-current 0} 56 | :ledger {:chronicle "" 57 | :bashing 0 58 | :lethal 0 59 | :aggravated 0 60 | :mana 5 61 | :size-current 5 62 | :armor {:bashing 0 63 | :lethal 0 64 | :aggravated 0} 65 | :spells-active 0 66 | :spells-cast-upon-self 0 67 | :spells-protective 0 68 | :nimbus :none 69 | :paradox-marks :none} 70 | :historicProperties [1 2 3 4]}) 71 | 72 | (def example {:scalar-value 42 73 | :list-of-scalars [1 4 9 16] 74 | :list-of-datastructures ["this" "is" "a" "heterogeneous" "list" #{:with :nested :stuff}] 75 | :table tbl 76 | :matrix [[1 2 3] ["a" "b" "c"] [1.2 1/4 4.5]] 77 | :conformed-data (s/conform :dsui.core/ds uni) 78 | :small-example uni 79 | :larger-example mage 80 | }) 81 | 82 | (defn -main [& args] 83 | (swing/dsui example (fn [_] 84 | (println "Exiting!") 85 | (System/exit 0)))) 86 | 87 | 88 | 89 | ;; How to use watch-ui: 90 | (comment 91 | ;; Create a ref 92 | (defonce state (atom [])) 93 | 94 | ;; Create the watch-ui. When working at the REPL use defonce to avoid adding multiple UIs/watchers to the ref. 95 | (defonce ui (swing/watch-ui state)) 96 | 97 | ;; Swap new data into the atom (REPL workflow) 98 | (reset! state '[1 2 3 "A" "B" "C" + - * /]) 99 | ;; Or just have it display your app's changing state (e.g. in an atom). 100 | ) 101 | 102 | 103 | 104 | ;; primitives currently not supported 105 | #_(swing/dsui 42) 106 | 107 | ;; displays the last exception as a ui 108 | #_(-> *e Throwable->map swing/dsui) 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /src/dsui/swing.clj: -------------------------------------------------------------------------------- 1 | (ns dsui.swing 2 | (:require [clojure.spec.alpha :as s] 3 | [dsui.core :as core] 4 | [dsui.ui :as ui]) 5 | (:import [java.util Vector Collection] 6 | [javax.swing SwingUtilities JFrame JLabel JPanel JTextField JTabbedPane JTable JList DefaultListModel JScrollPane] 7 | [javax.swing.table DefaultTableModel] 8 | [java.awt.event KeyAdapter WindowAdapter] 9 | [java.awt Container GridBagLayout GridBagConstraints])) 10 | 11 | (defn ^JFrame frame [title close-f] 12 | (doto (new JFrame) 13 | (.setSize (new java.awt.Dimension 800 800)) 14 | (.addWindowListener (proxy [WindowAdapter] [] (windowClosing [e] (close-f e)))) 15 | (.setTitle title) 16 | (.setVisible true))) 17 | 18 | (defn add 19 | ([^Container p cs] 20 | (add p cs (repeat nil))) 21 | ([^Container p cs lds] 22 | (doall (map (fn [^Container c ld] (.add p c ld)) (flatten cs) (flatten lds))) 23 | p)) 24 | 25 | (defn panel [] 26 | (doto (new JPanel) 27 | (.setLayout (new GridBagLayout)))) 28 | 29 | (defn field [value] 30 | (doto (new JTextField (str value)) 31 | (.setEditable false))) 32 | 33 | (defn label [l] 34 | (new JLabel (str l))) 35 | 36 | (defn table [tm] 37 | (new JScrollPane (doto (new JTable tm) 38 | (.setEnabled false)))) 39 | 40 | (defn table-model [^Collection colnames rows] 41 | (let [^Vector cns (new Vector colnames) 42 | ^Collection rs (mapv (fn [^Collection r] (new Vector r)) rows) 43 | ^Vector content (new Vector rs)] 44 | (doto (new DefaultTableModel content cns) 45 | (.setColumnIdentifiers cns)))) 46 | 47 | (defn jlist [^DefaultListModel lm] 48 | (new JList lm)) 49 | 50 | (defn ^DefaultListModel list-model [coll] 51 | (let [lm (new DefaultListModel)] 52 | (doseq [el coll] (.addElement lm el)) 53 | lm)) 54 | 55 | (defn gb-constraints [{:keys [x y fl gw wx wy a]}] 56 | (let [gbc (new GridBagConstraints)] 57 | (set! (. gbc gridx) x) 58 | (set! (. gbc gridy) y) 59 | (set! (. gbc fill) fl) 60 | (set! (. gbc gridwidth) gw) 61 | (set! (. gbc weightx) wx) 62 | (set! (. gbc weighty) wy) 63 | (set! (. gbc anchor) a) 64 | gbc)) 65 | 66 | (defn panel-gbc [y] 67 | (gb-constraints {:x 0 :y y :fl GridBagConstraints/BOTH :gw (* 2 ui/colnumber) :wx 1.0 :wy 1.0 :a GridBagConstraints/PAGE_START})) 68 | 69 | (defn field-gbc [x y] 70 | (gb-constraints {:x x :y y :fl GridBagConstraints/HORIZONTAL :gw 1 :wx 1.0 :wy 0.0 :a GridBagConstraints/PAGE_START})) 71 | 72 | (defmulti create first) 73 | 74 | (defn tabbed-pane [m] 75 | (let [tpane (new JTabbedPane)] 76 | (doseq [[k v] m] 77 | (.addTab tpane (str k) (create v))) 78 | tpane)) 79 | 80 | 81 | 82 | (defmethod create :dsui.core/scalar [[_ ds]] 83 | (label ds)) 84 | 85 | (defmethod create :dsui.core/list-of-dss [[_ ds]] 86 | (let [m (into {} (map-indexed (fn [i v] [i v]) ds)) 87 | ^JTabbedPane tpane (tabbed-pane m)] 88 | (. tpane setTabPlacement JTabbedPane/LEFT) 89 | tpane)) 90 | 91 | (defmethod create :dsui.core/labeled-ds [[_ ds]] 92 | (let [m (into {} [ds]) 93 | ^JTabbedPane tpane (tabbed-pane m)] 94 | (. tpane setTabPlacement JTabbedPane/LEFT) 95 | tpane)) 96 | 97 | (defmethod create :dsui.core/entity [[_ ds]] 98 | (let [p (panel) 99 | layout-cols (* 2 ui/colnumber) 100 | fields (filter #(= :dsui.core/scalar (first (second %))) ds) 101 | nested-uis (filter #(= :dsui.core/nested-ds (first (second %))) ds)] 102 | (add p 103 | (for [[kw childDs] fields] 104 | [(label kw) (create childDs)]) 105 | (for [i (range (* 2 (count fields)))] 106 | (field-gbc (mod i layout-cols) (quot i layout-cols)))) 107 | (add p [(tabbed-pane (into {} nested-uis))] 108 | [(panel-gbc (inc (quot (count fields) ui/colnumber)))]) 109 | p)) 110 | 111 | (defmethod create :dsui.core/table-of-entities [[_ ds]] 112 | (table (table-model (keys (first ds)) (map vals ds)))) 113 | 114 | (defmethod create :dsui.core/matrix [[_ ds]] 115 | (table (table-model (range (count (first ds))) ds))) 116 | 117 | (defmethod create :dsui.core/list-of-scalars [[_ ds]] 118 | (jlist (list-model ds))) 119 | 120 | (defmethod create :dsui.core/scalar [[_ ds]] 121 | (field ds)) 122 | 123 | (defmethod create :dsui.core/nested-ds [[_ ds]] 124 | (create ds)) 125 | 126 | (defn dsui-content 127 | [ds] 128 | (create (s/conform :dsui.core/ds ds))) 129 | 130 | (defn refresh [frm] 131 | (. SwingUtilities invokeLater (fn [] (. SwingUtilities updateComponentTreeUI frm)))) 132 | 133 | (s/fdef dsui 134 | :args :dsui.core/ds 135 | :ret any?) 136 | (defn dsui 137 | "Creates a read-only, form-based swing UI for an arbitrary nested data structure." 138 | ([ds] 139 | (dsui ds #(print "Exiting..." %))) 140 | ([ds close-f] 141 | (doto (frame "DS-UI" close-f) 142 | (.setContentPane (dsui-content ds)) 143 | refresh))) 144 | 145 | 146 | (defn conform-ui 147 | "Shows how the data conformed to the spec. Useful for specs with choices (via s/or) or labeled values (vis s/cat). If the data does not conform, displays the explanation (explain-data) as a ui." 148 | [spec data] 149 | (let [conf (s/conform spec data)] 150 | (if (= :clojure.spec.alpha/invalid conf) 151 | (dsui (s/explain-data spec data)) 152 | (dsui conf)))) 153 | 154 | 155 | (defn watch-ui 156 | "Watches the ref and updates itself to mirror the ref's value whenever it changes." 157 | [ref] 158 | (let [frame (dsui @ref)] 159 | (add-watch ref :dsui (fn [key ref old-data new-data] 160 | (doto frame 161 | (.setContentPane (dsui-content new-data)) 162 | refresh))))) 163 | 164 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' from 19 | a Contributor if it was added to the Program by such Contributor itself or 20 | anyone acting on such Contributor's behalf. Contributions do not include 21 | additions to the Program which: (i) are separate modules of software 22 | distributed in conjunction with the Program under their own license 23 | agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this Agreement. 32 | 33 | "Recipient" means anyone who receives the Program under this Agreement, 34 | including all Contributors. 35 | 36 | 2. GRANT OF RIGHTS 37 | a) Subject to the terms of this Agreement, each Contributor hereby grants 38 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 39 | reproduce, prepare derivative works of, publicly display, publicly perform, 40 | distribute and sublicense the Contribution of such Contributor, if any, and 41 | such derivative works, in source code and object code form. 42 | b) Subject to the terms of this Agreement, each Contributor hereby grants 43 | Recipient a non-exclusive, worldwide, royalty-free patent license under 44 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 45 | transfer the Contribution of such Contributor, if any, in source code and 46 | object code form. This patent license shall apply to the combination of the 47 | Contribution and the Program if, at the time the Contribution is added by 48 | the Contributor, such addition of the Contribution causes such combination 49 | to be covered by the Licensed Patents. The patent license shall not apply 50 | to any other combinations which include the Contribution. No hardware per 51 | se is licensed hereunder. 52 | c) Recipient understands that although each Contributor grants the licenses to 53 | its Contributions set forth herein, no assurances are provided by any 54 | Contributor that the Program does not infringe the patent or other 55 | intellectual property rights of any other entity. Each Contributor 56 | disclaims any liability to Recipient for claims brought by any other entity 57 | based on infringement of intellectual property rights or otherwise. As a 58 | condition to exercising the rights and licenses granted hereunder, each 59 | Recipient hereby assumes sole responsibility to secure any other 60 | intellectual property rights needed, if any. For example, if a third party 61 | patent license is required to allow Recipient to distribute the Program, it 62 | is Recipient's responsibility to acquire that license before distributing 63 | the Program. 64 | d) Each Contributor represents that to its knowledge it has sufficient 65 | copyright rights in its Contribution, if any, to grant the copyright 66 | license set forth in this Agreement. 67 | 68 | 3. REQUIREMENTS 69 | 70 | A Contributor may choose to distribute the Program in object code form under its 71 | own license agreement, provided that: 72 | 73 | a) it complies with the terms and conditions of this Agreement; and 74 | b) its license agreement: 75 | i) effectively disclaims on behalf of all Contributors all warranties and 76 | conditions, express and implied, including warranties or conditions of 77 | title and non-infringement, and implied warranties or conditions of 78 | merchantability and fitness for a particular purpose; 79 | ii) effectively excludes on behalf of all Contributors all liability for 80 | damages, including direct, indirect, special, incidental and 81 | consequential damages, such as lost profits; 82 | iii) states that any provisions which differ from this Agreement are offered 83 | by that Contributor alone and not by any other party; and 84 | iv) states that source code for the Program is available from such 85 | Contributor, and informs licensees how to obtain it in a reasonable 86 | manner on or through a medium customarily used for software exchange. 87 | 88 | When the Program is made available in source code form: 89 | 90 | a) it must be made available under this Agreement; and 91 | b) a copy of this Agreement must be included with each copy of the Program. 92 | Contributors may not remove or alter any copyright notices contained within 93 | the Program. 94 | 95 | Each Contributor must identify itself as the originator of its Contribution, if 96 | any, in a manner that reasonably allows subsequent Recipients to identify the 97 | originator of the Contribution. 98 | 99 | 4. COMMERCIAL DISTRIBUTION 100 | 101 | Commercial distributors of software may accept certain responsibilities with 102 | respect to end users, business partners and the like. While this license is 103 | intended to facilitate the commercial use of the Program, the Contributor who 104 | includes the Program in a commercial product offering should do so in a manner 105 | which does not create potential liability for other Contributors. Therefore, if 106 | a Contributor includes the Program in a commercial product offering, such 107 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 108 | every other Contributor ("Indemnified Contributor") against any losses, damages 109 | and costs (collectively "Losses") arising from claims, lawsuits and other legal 110 | actions brought by a third party against the Indemnified Contributor to the 111 | extent caused by the acts or omissions of such Commercial Contributor in 112 | connection with its distribution of the Program in a commercial product 113 | offering. The obligations in this section do not apply to any claims or Losses 114 | relating to any actual or alleged intellectual property infringement. In order 115 | to qualify, an Indemnified Contributor must: a) promptly notify the Commercial 116 | Contributor in writing of such claim, and b) allow the Commercial Contributor to 117 | control, and cooperate with the Commercial Contributor in, the defense and any 118 | related settlement negotiations. The Indemnified Contributor may participate in 119 | any such claim at its own expense. 120 | 121 | For example, a Contributor might include the Program in a commercial product 122 | offering, Product X. That Contributor is then a Commercial Contributor. If that 123 | Commercial Contributor then makes performance claims, or offers warranties 124 | related to Product X, those performance claims and warranties are such 125 | Commercial Contributor's responsibility alone. Under this section, the 126 | Commercial Contributor would have to defend claims against the other 127 | Contributors related to those performance claims and warranties, and if a court 128 | requires any other Contributor to pay any damages as a result, the Commercial 129 | Contributor must pay those damages. 130 | 131 | 5. NO WARRANTY 132 | 133 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 134 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 135 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 136 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 137 | Recipient is solely responsible for determining the appropriateness of using and 138 | distributing the Program and assumes all risks associated with its exercise of 139 | rights under this Agreement , including but not limited to the risks and costs 140 | of program errors, compliance with applicable laws, damage to or loss of data, 141 | programs or equipment, and unavailability or interruption of operations. 142 | 143 | 6. DISCLAIMER OF LIABILITY 144 | 145 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 146 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 147 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 148 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 149 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 150 | OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS 151 | GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 152 | 153 | 7. GENERAL 154 | 155 | If any provision of this Agreement is invalid or unenforceable under applicable 156 | law, it shall not affect the validity or enforceability of the remainder of the 157 | terms of this Agreement, and without further action by the parties hereto, such 158 | provision shall be reformed to the minimum extent necessary to make such 159 | provision valid and enforceable. 160 | 161 | If Recipient institutes patent litigation against any entity (including a 162 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 163 | (excluding combinations of the Program with other software or hardware) 164 | infringes such Recipient's patent(s), then such Recipient's rights granted under 165 | Section 2(b) shall terminate as of the date such litigation is filed. 166 | 167 | All Recipient's rights under this Agreement shall terminate if it fails to 168 | comply with any of the material terms or conditions of this Agreement and does 169 | not cure such failure in a reasonable period of time after becoming aware of 170 | such noncompliance. If all Recipient's rights under this Agreement terminate, 171 | Recipient agrees to cease use and distribution of the Program as soon as 172 | reasonably practicable. However, Recipient's obligations under this Agreement 173 | and any licenses granted by Recipient relating to the Program shall continue and 174 | survive. 175 | 176 | Everyone is permitted to copy and distribute copies of this Agreement, but in 177 | order to avoid inconsistency the Agreement is copyrighted and may only be 178 | modified in the following manner. The Agreement Steward reserves the right to 179 | publish new versions (including revisions) of this Agreement from time to time. 180 | No one other than the Agreement Steward has the right to modify this Agreement. 181 | The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation 182 | may assign the responsibility to serve as the Agreement Steward to a suitable 183 | separate entity. Each new version of the Agreement will be given a 184 | distinguishing version number. The Program (including Contributions) may always 185 | be distributed subject to the version of the Agreement under which it was 186 | received. In addition, after a new version of the Agreement is published, 187 | Contributor may elect to distribute the Program (including its Contributions) 188 | under the new version. Except as expressly stated in Sections 2(a) and 2(b) 189 | above, Recipient receives no rights or licenses to the intellectual property of 190 | any Contributor under this Agreement, whether expressly, by implication, 191 | estoppel or otherwise. All rights in the Program not expressly granted under 192 | this Agreement are reserved. 193 | 194 | This Agreement is governed by the laws of the State of New York and the 195 | intellectual property laws of the United States of America. No party to this 196 | Agreement will bring a legal action under this Agreement more than one year 197 | after the cause of action arose. Each party waives its rights to a jury trial in 198 | any resulting litigation. 199 | --------------------------------------------------------------------------------