1 | /*!
|
2 | * Copyright (c) 2017-2018 by The Funfix Project Developers.
|
3 | * Some rights reserved.
|
4 | *
|
5 | * Licensed under the Apache License, Version 2.0 (the "License");
|
6 | * you may not use this file except in compliance with the License.
|
7 | * You may obtain a copy of the License at
|
8 | *
|
9 | * http://www.apache.org/licenses/LICENSE-2.0
|
10 | *
|
11 | * Unless required by applicable law or agreed to in writing, software
|
12 | * distributed under the License is distributed on an "AS IS" BASIS,
|
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14 | * See the License for the specific language governing permissions and
|
15 | * limitations under the License.
|
16 | */
|
17 | /**
|
18 | * Test if the given reference is a value object.
|
19 | *
|
20 | * Value objects are objects that implement the [[IEquals]]
|
21 | * interface.
|
22 | *
|
23 | * @param ref is the reference to test
|
24 | */
|
25 | export function isValueObject(ref) {
|
26 | return !!(ref &&
|
27 | typeof ref.equals === "function" &&
|
28 | typeof ref.hashCode === "function");
|
29 | }
|
30 | /**
|
31 | * Tests for universal equality.
|
32 | *
|
33 | * First attempting a reference check with `===`,
|
34 | * after which it tries to fallback on [[IEquals]], if the
|
35 | * left-hand side is implementing it.
|
36 | *
|
37 | * ```typescript
|
38 | * equals(10, 10) // true, because 10 === 10
|
39 | *
|
40 | * class Box implements IEquals<Box> {
|
41 | * constructor(value: number) { this.value = value }
|
42 | *
|
43 | * equals(other) { return this.value === other.value }
|
44 | * hashCode() { return this.value << 2 }
|
45 | * }
|
46 | *
|
47 | * // false, because they are not the same reference
|
48 | * new Box(10) === new Box(10)
|
49 | *
|
50 | * // true, because `Box#equals` gets called
|
51 | * equals(new Box(10), new Box(10))
|
52 | * ```
|
53 | */
|
54 | export function is(lh, rh) {
|
55 | if (lh === rh || (lh !== lh && rh !== rh)) {
|
56 | return true;
|
57 | }
|
58 | if (!lh || !rh) {
|
59 | return false;
|
60 | }
|
61 | /* istanbul ignore else */
|
62 | /* tslint:disable-next-line:strict-type-predicates */
|
63 | if (typeof lh.valueOf === "function" && typeof rh.valueOf === "function") {
|
64 | const lh2 = lh.valueOf();
|
65 | const rh2 = rh.valueOf();
|
66 | if (lh2 === rh2 || (lh2 !== lh2 && rh2 !== rh2)) {
|
67 | return true;
|
68 | }
|
69 | if (!lh2 || !rh2) {
|
70 | return false;
|
71 | }
|
72 | }
|
73 | // noinspection PointlessBooleanExpressionJS
|
74 | return !!(isValueObject(lh) &&
|
75 | lh.equals(rh));
|
76 | }
|
77 | /** Alias for [[is]]. */
|
78 | export function equals(lh, rh) {
|
79 | return is(lh, rh);
|
80 | }
|
81 | /**
|
82 | * Returns a `Setoid` type-class instance that depends
|
83 | * universal equality, as defined by {@link is}.
|
84 | */
|
85 | export const universalSetoid = { equals };
|
86 | /**
|
87 | * Universal hash-code function.
|
88 | *
|
89 | * Depending on the given value, it calculates the hash-code like so:
|
90 | *
|
91 | * 1. if it's a `number`, then it gets truncated
|
92 | * to an integer and returned
|
93 | * 2. if it's a "value object" (see [[isValueObject]]), then
|
94 | * its `hashCode` is used
|
95 | * 3. if a `valueOf()` function is provided, then the
|
96 | * `hashCode` gets recursively invoked on its result
|
97 | * 4. if all else fails, the value gets coerced to a `String`
|
98 | * and a hash code is calculated using [[hashCodeOfString]]
|
99 | *
|
100 | * @param ref is the value to use for calculating a hash code
|
101 | * @return an integer with the aforementioned properties
|
102 | */
|
103 | export function hashCode(ref) {
|
104 | if (typeof ref === "number") {
|
105 | return ref & ref;
|
106 | }
|
107 | /* istanbul ignore else */
|
108 | if (typeof ref.valueOf === "function") {
|
109 | const v = ref.valueOf();
|
110 | if (v !== ref)
|
111 | return hashCode(v);
|
112 | }
|
113 | if (isValueObject(ref)) {
|
114 | return ref.hashCode();
|
115 | }
|
116 | return hashCodeOfString(String(ref));
|
117 | }
|
118 | /**
|
119 | * Calculates a hash code out of any string.
|
120 | */
|
121 | export function hashCodeOfString(str) {
|
122 | let hash = 0;
|
123 | /* tslint:disable-next-line:strict-type-predicates */
|
124 | if (str == null || str.length === 0)
|
125 | return hash;
|
126 | for (let i = 0; i < str.length; i++) {
|
127 | const character = str.charCodeAt(i);
|
128 | hash = ((hash << 5) - hash) + character;
|
129 | hash = hash & hash; // Convert to 32bit integer
|
130 | }
|
131 | return hash;
|
132 | }
|
133 | /** The identity function. */
|
134 | export function id(a) {
|
135 | return a;
|
136 | }
|
137 | /**
|
138 | * Utility function for implementing mixins, based on the
|
139 | * [TypeScript Mixins]{@link https://www.typescriptlang.org/docs/handbook/mixins.html}
|
140 | * documentation.
|
141 | *
|
142 | * Sample:
|
143 | *
|
144 | * ```typescript
|
145 | * class Disposable { ... }
|
146 | * class Activatable { ... }
|
147 | * class SmartObject implements Disposable, Activatable { ... }
|
148 | *
|
149 | * applyMixins(SmartObject, [Disposable, Activatable]);
|
150 | * ```
|
151 | *
|
152 | * Using `implements` instead of `extends` for base classes
|
153 | * will make the type system treat them like interfaces instead of
|
154 | * classes. And by `applyMixins` we can also supply global
|
155 | * implementations for the non-abstract members.
|
156 | */
|
157 | export function applyMixins(derivedCtor, baseCtors) {
|
158 | baseCtors.forEach(baseCtor => {
|
159 | Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
|
160 | if (!derivedCtor.prototype[name])
|
161 | derivedCtor.prototype[name] = baseCtor.prototype[name];
|
162 | });
|
163 | });
|
164 | }
|
165 | //# sourceMappingURL=std.js.map |
\ | No newline at end of file |