crypto/fips/org.argeo.tp.crypto \
org.argeo.tp \
org.argeo.tp.httpd \
-osgi/api/org.argeo.tp.osgi \
+osgi/equinox/org.argeo.tp.osgi \
osgi/equinox/org.argeo.tp.eclipse \
swt/rap/org.argeo.tp.swt \
$(A2_CATEGORY) \
package org.argeo.api.acr;
+import javax.xml.namespace.NamespaceContext;
+
/**
* An attribute type MUST consistently parse a string to an object so that
* <code>parse(obj.toString()).equals(obj)</code> is verified.
*/
public interface AttributeFormatter<T> {
/** Parses a String to a Java object. */
- T parse(String str) throws IllegalArgumentException;
+ default T parse(String str) throws IllegalArgumentException {
+ return parse(RuntimeNamespaceContext.getNamespaceContext(), str);
+ }
+
+ /**
+ * Parses a String to a Java object, possibly using the namespace context to
+ * resolve QName or CURIE.
+ */
+ T parse(NamespaceContext namespaceContext, String str) throws IllegalArgumentException;
/** Default implementation returns {@link Object#toString()} on the argument. */
default String format(T obj) {
* A semi-structured content, with attributes, within a hierarchical structure.
*/
public interface Content extends Iterable<Content>, Map<QName, Object> {
+ /** The base of a repository path. */
+ String ROOT_PATH = "/";
QName getName();
}
List<A> res = getMultiple(key, type);
return res;
-// if (res == null)
-// return null;
-// else {
-// if (res.isEmpty())
-// throw new IllegalStateException("Metadata " + key + " is not availabel as list of type " + type);
-// return res.get();
-// }
}
/*
* CONTENT OPERATIONS
*/
-// default CompletionStage<Content> edit(Consumer<Content> work) {
-// return CompletableFuture.supplyAsync(() -> {
-// work.accept(this);
-// return this;
-// }).minimalCompletionStage();
-// }
-
Content add(QName name, QName... classes);
default Content add(String name, QName... classes) {
return res;
}
+ default List<Content> children(QNamed name) {
+ return children(name.qName());
+ }
+
+ default Optional<Content> soleChild(QNamed name) {
+ return soleChild(name.qName());
+ }
+
default Optional<Content> soleChild(QName name) {
List<Content> res = children(name);
if (res.isEmpty())
/*
* ATTR AS STRING
*/
+ /**
+ * Convenience method returning an attribute as a {@link String}.
+ *
+ * @param key the attribute name
+ * @return the attribute value as a {@link String} or <code>null</code>.
+ *
+ * @see Object#toString()
+ */
default String attr(QName key) {
- // TODO check String type?
- Object obj = get(key);
- if (obj == null)
- return null;
- return obj.toString();
+ return get(key, String.class).orElse(null);
}
+ /**
+ * Convenience method returning an attribute as a {@link String}.
+ *
+ * @param key the attribute name
+ * @return the attribute value as a {@link String} or <code>null</code>.
+ *
+ * @see Object#toString()
+ */
default String attr(QNamed key) {
return attr(key.qName());
}
+ /**
+ * Convenience method returning an attribute as a {@link String}.
+ *
+ * @param key the attribute name
+ * @return the attribute value as a {@link String} or <code>null</code>.
+ *
+ * @see Object#toString()
+ */
default String attr(String key) {
return attr(unqualified(key));
}
-//
-// default String attr(Object key) {
-// return key != null ? attr(key.toString()) : attr(null);
-// }
-//
-// default <A> A get(Object key, Class<A> clss) {
-// return key != null ? get(key.toString(), clss) : get(null, clss);
-// }
+
+ /*
+ * CONTEXT
+ */
+ /**
+ * A content within this repository
+ *
+ * @param path either an absolute path or a path relative to this content
+ */
+ Optional<Content> getContent(String path);
/*
* EXPERIMENTAL UNSUPPORTED
*/
@Override
- public String toString() {
+ public final String toString() {
return toQNameString();
}
import java.util.Locale;
import java.util.concurrent.CompletionStage;
import java.util.function.Consumer;
-import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.security.auth.Subject;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
-import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
+import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
/**
// (e.g. optional primitives)
DATE_TIME(Instant.class, W3C_XML_SCHEMA_NS_URI, "dateTime", new InstantFormatter()), //
UUID(UUID.class, ArgeoNamespace.CR_NAMESPACE_URI, "uuid", new UuidFormatter()), //
+ QNAME(QName.class, W3C_XML_SCHEMA_NS_URI, "QName", new QNameFormatter()), //
ANY_URI(URI.class, W3C_XML_SCHEMA_NS_URI, "anyUri", new UriFormatter()), //
STRING(String.class, W3C_XML_SCHEMA_NS_URI, "string", new StringFormatter()), //
;
qName = new ContentName(namespaceUri, localName, RuntimeNamespaceContext.getNamespaceContext());
}
- public QName getqName() {
+ public QName getQName() {
return qName;
}
/** Default parsing procedure from a String to an object. */
public static Object parse(String str) {
+ return parse(RuntimeNamespaceContext.getNamespaceContext(), str);
+ }
+
+ /** Default parsing procedure from a String to an object. */
+ public static Object parse(NamespaceContext namespaceContext, String str) {
if (str == null)
throw new IllegalArgumentException("String cannot be null");
// order IS important
try {
if (str.length() == 4 || str.length() == 5)
- return BOOLEAN.getFormatter().parse(str);
+ return BOOLEAN.getFormatter().parse(namespaceContext, str);
} catch (IllegalArgumentException e) {
// silent
}
try {
- return INTEGER.getFormatter().parse(str);
+ return INTEGER.getFormatter().parse(namespaceContext, str);
} catch (IllegalArgumentException e) {
// silent
}
try {
- return LONG.getFormatter().parse(str);
+ return LONG.getFormatter().parse(namespaceContext, str);
} catch (IllegalArgumentException e) {
// silent
}
try {
- return DOUBLE.getFormatter().parse(str);
+ return DOUBLE.getFormatter().parse(namespaceContext, str);
} catch (IllegalArgumentException e) {
// silent
}
try {
- return DATE_TIME.getFormatter().parse(str);
+ return DATE_TIME.getFormatter().parse(namespaceContext, str);
} catch (IllegalArgumentException e) {
// silent
}
try {
if (str.length() == 36)
- return UUID.getFormatter().parse(str);
+ return UUID.getFormatter().parse(namespaceContext, str);
+ } catch (IllegalArgumentException e) {
+ // silent
+ }
+
+ // CURIE
+ if (str.startsWith("[") && str.endsWith("]")) {
+ try {
+ if (str.indexOf(":") >= 0) {
+ QName qName = (QName) QNAME.getFormatter().parse(namespaceContext, str);
+ return (java.net.URI) ANY_URI.getFormatter().parse(qName.getNamespaceURI() + qName.getLocalPart());
+ }
+ } catch (IllegalArgumentException e) {
+ // silent
+ }
+ }
+
+ try {
+ if (str.indexOf(":") >= 0) {
+ QName qName = (QName) QNAME.getFormatter().parse(namespaceContext, str);
+ // note: this QName may not be valid
+ // note: CURIE should be explicitly defined with surrounding brackets
+ return qName;
+ }
} catch (IllegalArgumentException e) {
// silent
}
try {
- java.net.URI uri = (java.net.URI) ANY_URI.getFormatter().parse(str);
+ java.net.URI uri = (java.net.URI) ANY_URI.getFormatter().parse(namespaceContext, str);
if (uri.getScheme() != null)
return uri;
String path = uri.getPath();
// see https://www.oreilly.com/library/view/xml-schema/0596002521/re91.html
// default
- return STRING.getFormatter().parse(str);
+ return STRING.getFormatter().parse(namespaceContext, str);
}
/**
* object.
*
*/
- @SuppressWarnings("unchecked")
public static <T> Optional<T> cast(Class<T> clss, Object value) {
- // TODO Or should we?
- Objects.requireNonNull(value, "Cannot cast a null value");
+ return cast(RuntimeNamespaceContext.getNamespaceContext(), clss, value);
+ }
+
+ /**
+ * Cast well know java types based on {@link Object#toString()} of the provided
+ * object.
+ *
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> Optional<T> cast(NamespaceContext namespaceContext, Class<T> clss, Object value) {
+ // if value is null, optional is empty
+ if (value == null)
+ return Optional.empty();
+
+ // if a default has been explicitly requested by passing Object.class
+ // we parse the related String
+ if (clss.isAssignableFrom(Object.class)) {
+ return Optional.of((T) parse(value.toString()));
+ }
+
+ // if value can be cast directly, let's do it
+ if (value.getClass().isAssignableFrom(clss)) {
+ return Optional.of(((T) value));
+ }
+
+ // let's cast between various numbers (possibly losing precision)
+ if (value instanceof Number number) {
+ if (Long.class.isAssignableFrom(clss))
+ return Optional.of((T) (Long) number.longValue());
+ else if (Integer.class.isAssignableFrom(clss))
+ return Optional.of((T) (Integer) number.intValue());
+ else if (Double.class.isAssignableFrom(clss))
+ return Optional.of((T) (Double) number.doubleValue());
+ }
+
+ // let's now try with the string representation
+ String strValue = value instanceof String ? (String) value : value.toString();
+
if (String.class.isAssignableFrom(clss)) {
- return Optional.of((T) value.toString());
+ return Optional.of((T) strValue);
+ }
+ if (QName.class.isAssignableFrom(clss)) {
+ return Optional.of((T) NamespaceUtils.parsePrefixedName(namespaceContext, strValue));
}
// Numbers
else if (Long.class.isAssignableFrom(clss)) {
if (value instanceof Long)
return Optional.of((T) value);
- return Optional.of((T) Long.valueOf(value.toString()));
+ return Optional.of((T) Long.valueOf(strValue));
} else if (Integer.class.isAssignableFrom(clss)) {
if (value instanceof Integer)
return Optional.of((T) value);
- return Optional.of((T) Integer.valueOf(value.toString()));
+ return Optional.of((T) Integer.valueOf(strValue));
} else if (Double.class.isAssignableFrom(clss)) {
if (value instanceof Double)
return Optional.of((T) value);
- return Optional.of((T) Double.valueOf(value.toString()));
+ return Optional.of((T) Double.valueOf(strValue));
}
- // Numbers
-// else if (Number.class.isAssignableFrom(clss)) {
-// if (value instanceof Number)
-// return Optional.of((T) value);
-// return Optional.of((T) Number.valueOf(value.toString()));
-// }
- return Optional.empty();
+
+ // let's now try to parse the string representation to a well-known type
+ Object parsedValue = parse(strValue);
+ if (parsedValue.getClass().isAssignableFrom(clss)) {
+ return Optional.of(((T) value));
+ }
+ throw new ClassCastException("Cannot convert " + value.getClass() + " to " + clss);
}
/** Utility to convert a data: URI to bytes. */
* contract than {@link Boolean#parseBoolean(String)}.
*/
@Override
- public Boolean parse(String str) throws IllegalArgumentException {
+ public Boolean parse(NamespaceContext namespaceContext, String str) throws IllegalArgumentException {
if ("true".equals(str))
return Boolean.TRUE;
if ("false".equals(str))
static class IntegerFormatter implements AttributeFormatter<Integer> {
@Override
- public Integer parse(String str) throws NumberFormatException {
+ public Integer parse(NamespaceContext namespaceContext, String str) throws NumberFormatException {
return Integer.parseInt(str);
}
}
static class LongFormatter implements AttributeFormatter<Long> {
@Override
- public Long parse(String str) throws NumberFormatException {
+ public Long parse(NamespaceContext namespaceContext, String str) throws NumberFormatException {
return Long.parseLong(str);
}
}
static class DoubleFormatter implements AttributeFormatter<Double> {
@Override
- public Double parse(String str) throws NumberFormatException {
+ public Double parse(NamespaceContext namespaceContext, String str) throws NumberFormatException {
return Double.parseDouble(str);
}
}
static class InstantFormatter implements AttributeFormatter<Instant> {
@Override
- public Instant parse(String str) throws IllegalArgumentException {
+ public Instant parse(NamespaceContext namespaceContext, String str) throws IllegalArgumentException {
try {
return Instant.parse(str);
} catch (DateTimeParseException e) {
static class UuidFormatter implements AttributeFormatter<UUID> {
@Override
- public UUID parse(String str) throws IllegalArgumentException {
+ public UUID parse(NamespaceContext namespaceContext, String str) throws IllegalArgumentException {
return java.util.UUID.fromString(str);
}
}
static class UriFormatter implements AttributeFormatter<URI> {
@Override
- public URI parse(String str) throws IllegalArgumentException {
+ public URI parse(NamespaceContext namespaceContext, String str) throws IllegalArgumentException {
try {
return new URI(str);
} catch (URISyntaxException e) {
}
+ static class QNameFormatter implements AttributeFormatter<QName> {
+
+ @Override
+ public QName parse(NamespaceContext namespaceContext, String str) throws IllegalArgumentException {
+ return NamespaceUtils.parsePrefixedName(namespaceContext, str);
+ }
+
+ }
+
static class StringFormatter implements AttributeFormatter<String> {
@Override
- public String parse(String str) {
+ public String parse(NamespaceContext namespaceContext, String str) {
return str;
}
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
+/** Static utilities around namespaces and prefixes. */
public class NamespaceUtils {
+ /**
+ * A {@link Comparator} ordering by namespace (full URI) and then local part.
+ */
+ public final static Comparator<QName> QNAME_COMPARATOR = new Comparator<QName>() {
+
+ @Override
+ public int compare(QName qn1, QName qn2) {
+ if (Objects.equals(qn1.getNamespaceURI(), qn2.getNamespaceURI())) {// same namespace
+ return qn1.getLocalPart().compareTo(qn2.getLocalPart());
+ } else {
+ return qn1.getNamespaceURI().compareTo(qn2.getNamespaceURI());
+ }
+ }
+ };
+
+ /**
+ * Return a {@link ContentName} from a prefixed name, using the default runtime
+ * {@link NamespaceContext}.
+ *
+ * @see RuntimeNamespaceContext#getNamespaceContext()
+ */
public static ContentName parsePrefixedName(String nameWithPrefix) {
return parsePrefixedName(RuntimeNamespaceContext.getNamespaceContext(), nameWithPrefix);
}
+ /**
+ * Return a {@link ContentName} from a prefixed name, using the provided
+ * {@link NamespaceContext}. Since {@link QName#QName(String, String)} does not
+ * validate, it can conceptually parse a CURIE.
+ *
+ * @see https://en.wikipedia.org/wiki/CURIE
+ */
public static ContentName parsePrefixedName(NamespaceContext nameSpaceContext, String nameWithPrefix) {
Objects.requireNonNull(nameWithPrefix, "Name cannot be null");
if (nameWithPrefix.charAt(0) == '{') {
String localName = nameWithPrefix.substring(index + 1);
String namespaceURI = nameSpaceContext.getNamespaceURI(prefix);
if (XMLConstants.NULL_NS_URI.equals(namespaceURI))
- throw new IllegalStateException("Prefix " + prefix + " is unbound.");
+ throw new IllegalArgumentException("Prefix " + prefix + " is unbound.");
return new ContentName(namespaceURI, localName, prefix);
}
+ /**
+ * The prefixed name of this {@link QName}, using the default runtime
+ * {@link NamespaceContext}.
+ *
+ * @see RuntimeNamespaceContext#getNamespaceContext()
+ */
public static String toPrefixedName(QName name) {
return toPrefixedName(RuntimeNamespaceContext.getNamespaceContext(), name);
}
+ /**
+ * The prefixed name of this {@link QName}, using the provided
+ * {@link NamespaceContext}.
+ */
public static String toPrefixedName(NamespaceContext nameSpaceContext, QName name) {
if (XMLConstants.NULL_NS_URI.equals(name.getNamespaceURI()))
return name.getLocalPart();
return prefix + ":" + name.getLocalPart();
}
- public final static Comparator<QName> QNAME_COMPARATOR = new Comparator<QName>() {
-
- @Override
- public int compare(QName qn1, QName qn2) {
- if (Objects.equals(qn1.getNamespaceURI(), qn2.getNamespaceURI())) {// same namespace
- return qn1.getLocalPart().compareTo(qn2.getLocalPart());
- } else {
- return qn1.getNamespaceURI().compareTo(qn2.getNamespaceURI());
- }
- }
-
- };
-
+ /**
+ * Whether thei {@link QName} has a namespace, that is its namespace is not
+ * {@link XMLConstants#NULL_NS_URI}.
+ */
public static boolean hasNamespace(QName qName) {
return !qName.getNamespaceURI().equals(XMLConstants.NULL_NS_URI);
}
- public static void checkNoPrefix(String unqualified) {
+ /** Throws an exception if the provided string has a prefix. */
+ public static void checkNoPrefix(String unqualified) throws IllegalArgumentException {
if (unqualified.indexOf(':') >= 0)
throw new IllegalArgumentException("Name " + unqualified + " has a prefix");
}
+ /**
+ * Create an unqualified {@link QName}, checking that the argument does not
+ * contain a prefix.
+ */
public static QName unqualified(String name) {
checkNoPrefix(name);
return new ContentName(XMLConstants.NULL_NS_URI, name, XMLConstants.DEFAULT_NS_PREFIX);
}
+ /**
+ * The common (fully qualified) representation of this name, as defined in
+ * {@link QName#toString()}. This should be used when a fully qualified
+ * representation is required, as subclasses of {@link QName} may override the
+ * {@link QName#toString()} method.
+ *
+ * @see ContentName#toString()
+ */
+ public static String toFullyQualified(QName name) {
+ if (name.getNamespaceURI().equals(XMLConstants.NULL_NS_URI)) {
+ return name.getLocalPart();
+ } else {
+ return "{" + name.getNamespaceURI() + "}" + name.getLocalPart();
+ }
+
+ }
+
/*
- * DEFAULT NAMESPACE CONTEXT OPERATIONS as specified in NamespaceContext
+ * STANDARD NAMESPACE CONTEXT OPERATIONS as specified in NamespaceContext
+ */
+ /**
+ * The standard prefix for well known namespaces as defined in
+ * {@link NamespaceContext}, or null if the namespace is not well-known. Helper
+ * method simplifying the implementation of a {@link NamespaceContext}.
+ *
+ * @see NamespaceContext#getPrefix(String)
*/
public static String getStandardPrefix(String namespaceURI) {
if (namespaceURI == null)
return null;
}
+ /**
+ * The standard prefixes for well known namespaces as defined in
+ * {@link NamespaceContext}, or null if the namespace is not well-known. Helper
+ * method simplifying the implementation of a {@link NamespaceContext}.
+ *
+ * @see NamespaceContext#getPrefixes(String)
+ */
public static Iterator<String> getStandardPrefixes(String namespaceURI) {
String prefix = getStandardPrefix(namespaceURI);
if (prefix == null)
return Collections.singleton(prefix).iterator();
}
+ /**
+ * The standard URI for well known prefixes as defined in
+ * {@link NamespaceContext}, or null if the prefix is not well-known. Helper
+ * method simplifying the implementation of a {@link NamespaceContext}.
+ *
+ * @see NamespaceContext#getNamespaceURI(String)
+ */
public static String getStandardNamespaceURI(String prefix) {
if (prefix == null)
throw new IllegalArgumentException("Prefix cannot be null");
return null;
}
+ /**
+ * The namespace URI for a given prefix, based on the provided mapping and the
+ * standard prefixes/URIs.
+ *
+ * @return the namespace URI for this prefix, or
+ * {@link XMLConstants#NULL_NS_URI} if unknown.
+ */
public static String getNamespaceURI(Function<String, String> mapping, String prefix) {
String namespaceURI = NamespaceUtils.getStandardNamespaceURI(prefix);
if (namespaceURI != null)
return XMLConstants.NULL_NS_URI;
}
+ /**
+ * The prefix for a given namespace URI, based on the provided mapping and the
+ * standard prefixes/URIs.
+ *
+ * @return the prefix for this namespace URI. What is returned or throws as
+ * exception if the prefix is not found depdnds on the mapping function.
+ */
public static String getPrefix(Function<String, String> mapping, String namespaceURI) {
String prefix = NamespaceUtils.getStandardPrefix(namespaceURI);
if (prefix != null)
return mapping.apply(namespaceURI);
}
+ /**
+ * The prefixes for a given namespace URI, based on the provided mapping and the
+ * standard prefixes/URIs.
+ *
+ * @return the prefixes for this namespace URI. What is returned or throws as
+ * exception if the prefix is not found depdnds on the mapping function.
+ */
public static Iterator<String> getPrefixes(Function<String, Set<String>> mapping, String namespaceURI) {
Iterator<String> standard = NamespaceUtils.getStandardPrefixes(namespaceURI);
if (standard != null)
return namespaceContext.getPrefix(getNamespace()) + ":" + localName();
}
+ /** This qualified named with its default prefix. If it is unqualified this method should be overridden, or QNamed.Unqualified be used. */
default String get() {
return getDefaultPrefix() + ":" + localName();
}
return XMLConstants.DEFAULT_NS_PREFIX;
}
+ @Override
+ default String get() {
+ return localName();
+ }
+
}
}
import javax.xml.namespace.NamespaceContext;
/**
- * Programmatically defined {@link NamespaceContext}, code contributing
- * namespaces MUST register here with a single default prefix.
+ * Programmatically defined {@link NamespaceContext}, which is valid at runtime
+ * (when the software is running). Code contributing namespaces MUST register
+ * here with a single default prefix, nad MUST make sure that stored data
+ * contains the fully qualified namespace URI.
*/
public class RuntimeNamespaceContext implements NamespaceContext {
public final static String XSD_DEFAULT_PREFIX = "xs";
private NavigableMap<String, String> prefixes = new TreeMap<>();
private NavigableMap<String, String> namespaces = new TreeMap<>();
+ /*
+ * NAMESPACE CONTEXT IMPLEMENTATION
+ */
+
@Override
- public String getPrefix(String namespaceURI) {
+ public String getPrefix(String namespaceURI) throws IllegalArgumentException {
return NamespaceUtils.getPrefix((ns) -> {
String prefix = namespaces.get(ns);
if (prefix == null)
- throw new IllegalStateException("Namespace " + ns + " is not registered.");
+ throw new IllegalArgumentException("Namespace " + ns + " is not registered.");
return prefix;
}, namespaceURI);
}
@Override
- public String getNamespaceURI(String prefix) {
+ public String getNamespaceURI(String prefix) throws IllegalArgumentException {
return NamespaceUtils.getNamespaceURI((p) -> {
String ns = prefixes.get(p);
if (ns == null)
- throw new IllegalStateException("Prefix " + p + " is not registered.");
+ throw new IllegalArgumentException("Prefix " + p + " is not registered.");
return ns;
}, prefix);
}
@Override
- public Iterator<String> getPrefixes(String namespaceURI) {
+ public Iterator<String> getPrefixes(String namespaceURI) throws IllegalArgumentException {
return Collections.singleton(getPrefix(namespaceURI)).iterator();
}
/*
* STATIC
*/
-
private final static RuntimeNamespaceContext INSTANCE = new RuntimeNamespaceContext();
static {
register(ArgeoNamespace.ROLE_NAMESPACE_URI, ArgeoNamespace.ROLE_DEFAULT_PREFIX);
}
+ /** The runtime namespace context instance. */
public static NamespaceContext getNamespaceContext() {
return INSTANCE;
}
+ /** The registered prefixes. */
public static Map<String, String> getPrefixes() {
return Collections.unmodifiableNavigableMap(INSTANCE.prefixes);
}
- public synchronized static void register(String namespaceURI, String prefix) {
+ /** Registers a namespace URI / default prefix mapping. */
+ public synchronized static void register(String namespaceURI, String defaultPrefix) {
NavigableMap<String, String> prefixes = INSTANCE.prefixes;
NavigableMap<String, String> namespaces = INSTANCE.namespaces;
- if (prefixes.containsKey(prefix)) {
- String ns = prefixes.get(prefix);
+ if (prefixes.containsKey(defaultPrefix)) {
+ String ns = prefixes.get(defaultPrefix);
if (ns.equals(namespaceURI))
return; // ignore silently
- throw new IllegalStateException("Prefix " + prefix + " is already registered with namespace URI " + ns);
+ throw new IllegalStateException(
+ "Prefix " + defaultPrefix + " is already registered with namespace URI " + ns);
}
if (namespaces.containsKey(namespaceURI)) {
String p = namespaces.get(namespaceURI);
- if (p.equals(prefix))
+ if (p.equals(defaultPrefix))
return; // ignore silently
throw new IllegalStateException("Namespace " + namespaceURI + " is already registered with prefix " + p);
}
- prefixes.put(prefix, namespaceURI);
- namespaces.put(namespaceURI, prefix);
+ prefixes.put(defaultPrefix, namespaceURI);
+ namespaces.put(namespaceURI, defaultPrefix);
}
}
package org.argeo.api.acr.search;
+/** AND filter based on the intersection composition. */
public class AndFilter extends ContentFilter<Intersection> {
public AndFilter() {
import javax.xml.namespace.QName;
-import org.argeo.api.acr.DName;
import org.argeo.api.acr.QNamed;
+/**
+ * A basic search mechanism modelled on WebDav basicsearch.
+ *
+ * @see http://www.webdav.org/specs/rfc5323.html
+ */
public class BasicSearch {
private List<QName> select = new ArrayList<>();
return this;
}
+ /**
+ * Convenience method, to search below this absolute path, with depth
+ * {@link Depth#INFINITTY}.
+ */
+ public BasicSearch from(String path) {
+ return from(URI.create(path), Depth.INFINITTY);
+ }
+
+ /** Search below this URI, with depth {@link Depth#INFINITTY}. */
public BasicSearch from(URI uri) {
return from(uri, Depth.INFINITTY);
}
+ /** Search below this URI, with this {@link Depth}. */
public BasicSearch from(URI uri, Depth depth) {
Objects.requireNonNull(uri);
Objects.requireNonNull(depth);
}
- static void main(String[] args) {
- BasicSearch search = new BasicSearch();
- search.select(DName.creationdate.qName()) //
- .from(URI.create("/test")) //
- .where((f) -> f.eq(DName.creationdate.qName(), ""));
- }
+// static void main(String[] args) {
+// BasicSearch search = new BasicSearch();
+// search.select(DName.creationdate.qName()) //
+// .from(URI.create("/test")) //
+// .where((f) -> f.eq(DName.creationdate.qName(), ""));
+// }
}
package org.argeo.api.acr.search;
+
+/** Marker interface for a composition of multiple constraints. */
interface Composition {
}
-
package org.argeo.api.acr.search;
+/** Marker interface for a constraint. */
public interface Constraint {
}
import javax.xml.namespace.QName;
-import org.argeo.api.acr.DName;
import org.argeo.api.acr.QNamed;
+/** A constraint filtering based ona given composition (and/or). */
public abstract class ContentFilter<COMPOSITION extends Composition> implements Constraint {
private Set<Constraint> constraintss = new HashSet<>();
}
- public static void main(String[] args) {
- AndFilter filter = new AndFilter();
- filter.eq(new QName("test"), "test").and().not().eq(new QName("type"), "integer");
-
- OrFilter unionFilter = new OrFilter();
- unionFilter.all((f) -> {
- f.eq(DName.displayname, "").and().eq(DName.creationdate, "");
- }).or().not().any((f) -> {
- f.eq(DName.creationdate, "").or().eq(DName.displayname, "");
- });
-
- }
+// public static void main(String[] args) {
+// AndFilter filter = new AndFilter();
+// filter.eq(new QName("test"), "test").and().not().eq(new QName("type"), "integer");
+//
+// OrFilter unionFilter = new OrFilter();
+// unionFilter.all((f) -> {
+// f.eq(DName.displayname, "").and().eq(DName.creationdate, "");
+// }).or().not().any((f) -> {
+// f.eq(DName.creationdate, "").or().eq(DName.displayname, "");
+// });
+//
+// }
}
package org.argeo.api.acr.search;
-class Intersection implements Composition {
+
+/** A composition which is the intersection of sets (AND). */
+public class Intersection implements Composition {
ContentFilter<Intersection> filter;
@SuppressWarnings("unchecked")
}
}
-
package org.argeo.api.acr.search;
+/** OR filter based on the union composition. */
public class OrFilter extends ContentFilter<Union> {
public OrFilter() {
package org.argeo.api.acr.search;
-class Union implements Composition {
+/** A composition which is the union of sets (OR). */
+public class Union implements Composition {
ContentFilter<Union> filter;
@SuppressWarnings("unchecked")
import javax.xml.namespace.NamespaceContext;
import org.argeo.api.acr.Content;
+import org.argeo.api.acr.ContentNotFoundException;
+import org.argeo.api.acr.ContentSession;
import org.argeo.api.acr.search.BasicSearch;
+/**
+ * A prover of {@link Content}, which can be mounted in a
+ * {@link ProvidedRepository}.
+ */
public interface ContentProvider extends NamespaceContext {
- ProvidedContent get(ProvidedSession session, String relativePath);
+ /**
+ * Return the content at this path, relative to the mount path.
+ *
+ * @return the content at this relative path, never <code>null</code>
+ * @throws ContentNotFoundException if there is no content at this relative path
+ */
+ ProvidedContent get(ProvidedSession session, String relativePath) throws ContentNotFoundException;
- boolean exists(ProvidedSession session, String relativePath);
+ /**
+ * Whether a content exist at his relative path. The default implementation call
+ * {@link #get(ProvidedSession, String)} and check whether a
+ * {@link ContentNotFoundException} is thrown or not. It should be overridden as
+ * soon as there is a mechanism to check existence before actually getting the
+ * content.
+ */
+ default boolean exists(ProvidedSession session, String relativePath) {
+ try {
+ get(session, relativePath);
+ return true;
+ } catch (ContentNotFoundException e) {
+ return false;
+ }
+ }
+ /** The absolute path where this provider is mounted. */
String getMountPath();
+ /**
+ * Search content within this provider. The default implementation throws an
+ * {@link UnsupportedOperationException}.
+ */
+ default Spliterator<Content> search(ProvidedSession session, BasicSearch search, String relPath) {
+ throw new UnsupportedOperationException();
+ }
+
+ /*
+ * EDITION
+ */
+ /** Switch this content (and its subtree) to editing mode. */
+ default void openForEdit(ProvidedSession session, String relativePath) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** Switch this content (and its subtree) to frozen mode. */
+ default void freeze(ProvidedSession session, String relativePath) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** Whether this content (and its subtree) are in editing mode. */
+ default boolean isOpenForEdit(ProvidedSession session, String relativePath) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Called when an edition cycle is completed. Does nothing by default.
+ *
+ * @see ContentSession#edit(java.util.function.Consumer)
+ */
+ default void persist(ProvidedSession session) {
+ }
+
/*
* NAMESPACE CONTEXT
*/
return prefixes.hasNext() ? prefixes.next() : null;
}
- default Spliterator<Content> search(ProvidedSession session, BasicSearch search, String relPath) {
- throw new UnsupportedOperationException();
- }
-
-// default ContentName parsePrefixedName(String nameWithPrefix) {
-// return NamespaceUtils.parsePrefixedName(this, nameWithPrefix);
-// }
-//
-// default String toPrefixedName(QName name) {
-// return NamespaceUtils.toPrefixedName(this, name);
-// }
-
}
package org.argeo.api.acr.spi;
+import java.util.Optional;
+
import org.argeo.api.acr.Content;
/** A {@link Content} implementation. */
public interface ProvidedContent extends Content {
- final static String ROOT_PATH = "/";
-
+ /** The related {@link ProvidedSession}. */
ProvidedSession getSession();
+ /** The {@link ContentProvider} this {@link Content} belongs to. */
ContentProvider getProvider();
+ /** Depth relative to the root of the repository. */
int getDepth();
+ /**
+ * Whether this is the root node of the related repository. Default checks
+ * whether <code>{@link #getDepth()} == 0</code>, but it can be optimised by
+ * implementations.
+ */
+ default boolean isRoot() {
+ return getDepth() == 0;
+ }
+
/**
* An opaque ID which is guaranteed to uniquely identify this content within the
* session return by {@link #getSession()}. Typically used for UI.
*/
String getSessionLocalId();
+ /**
+ * The {@link Content} within the same {@link ContentProvider} which can be used
+ * to mount another {@link ContentProvider}.
+ */
default ProvidedContent getMountPoint(String relativePath) {
throw new UnsupportedOperationException("This content doe not support mount");
}
- default ProvidedContent getContent(String path) {
- Content fileNode;
- if (path.startsWith(ROOT_PATH)) {// absolute
- fileNode = getSession().get(path);
+ @Override
+ default Optional<Content> getContent(String path) {
+ String absolutePath;
+ if (path.startsWith(Content.ROOT_PATH)) {// absolute
+ absolutePath = path;
} else {// relative
- String absolutePath = getPath() + '/' + path;
- fileNode = getSession().get(absolutePath);
+ absolutePath = getPath() + '/' + path;
}
- return (ProvidedContent) fileNode;
+ return getSession().exists(absolutePath) ? Optional.of(getSession().get(absolutePath)) : Optional.empty();
+ }
+
+ /*
+ * ACCESS
+ */
+ /** Whether the session has the right to access the parent. */
+ default boolean isParentAccessible() {
+ return true;
+ }
+
+ /** Whether the related session can open this content for edit. */
+ default boolean canEdit() {
+ return false;
}
}
void notifyModification(ProvidedContent content);
- UUID getUuid();
+ UUID uuid();
// Content getSessionRunDir();
final static String SESSION_UUID = "entryUUID";
final static String SESSION_LOCAL_ID = "uniqueIdentifier";
- UUID getUuid();
+ UUID uuid();
String getUserRole();
package org.argeo.api.cms.ux;
/** A 2D size. */
-public class Cms2DSize {
- private Integer width;
- private Integer height;
-
- public Cms2DSize() {
- }
-
- public Cms2DSize(Integer width, Integer height) {
- super();
- this.width = width;
- this.height = height;
- }
-
- public Integer getWidth() {
- return width;
- }
-
- public void setWidth(Integer width) {
- this.width = width;
- }
-
- public Integer getHeight() {
- return height;
- }
-
- public void setHeight(Integer height) {
- this.height = height;
- }
-
+public record Cms2DSize(int width, int height) {
@Override
public String toString() {
return Cms2DSize.class.getSimpleName() + "[" + width + "," + height + "]";
}
-
}
package org.argeo.api.cms.ux;
import java.io.InputStream;
+import java.net.URI;
/** Read and write access to images. */
public interface CmsImageManager<V, M> {
/** Load image in control */
- public Boolean load(M node, V control, Cms2DSize size);
+ public Boolean load(M node, V control, Cms2DSize size, URI link);
/** @return (0,0) if not available */
public Cms2DSize getImageSize(M node);
// SERVICES
void exception(Throwable e);
- CmsImageManager<?, ?> getImageManager();
+ <V,M> CmsImageManager<V, M> getImageManager();
boolean isAnonymous();
--- /dev/null
+package org.argeo.api.uuid;
+
+import java.util.UUID;
+
+/**
+ * An object identified by a {@link UUID}. Typically used to fasten indexing and
+ * comparisons of objects or records. THe method to implement is {@link #uuid()}
+ * so that any record with an <code>uuid</code> field can easily be enriched
+ * with this interface.
+ */
+public interface UuidIdentified {
+ /** The UUID identifier. */
+ UUID uuid();
+
+ /** The UUID identifier, for compatibility with beans accessors. */
+ default UUID getUuid() {
+ return uuid();
+ }
+
+ /**
+ * Helper to implement the equals method of an {@link UuidIdentified}.<br/>
+ *
+ * <pre>
+ * @Override
+ * public boolean equals(Object o) {
+ * return UuidIdentified.equals(this, o);
+ * }
+ * </pre>
+ */
+ static boolean equals(UuidIdentified uuidIdentified, Object o) {
+ assert uuidIdentified != null;
+ if (o == null)
+ return false;
+ if (uuidIdentified == o)
+ return true;
+ if (o instanceof UuidIdentified u)
+ return uuidIdentified.uuid().equals(u.uuid());
+ else
+ return false;
+ }
+
+ /**
+ * Helper to implement the hash code method of an {@link UuidIdentified}.<br/>
+ *
+ * <pre>
+ * @Override
+ * public int hashCode() {
+ * return UuidIdentified.hashCode(this);
+ * }
+ * </pre>
+ */
+ static int hashCode(UuidIdentified uuidIdentified) {
+ assert uuidIdentified != null;
+ return uuidIdentified.getUuid().hashCode();
+ }
+
+}
import org.argeo.api.cms.ux.CmsEditionEvent;
import org.argeo.api.cms.ux.CmsEditionListener;
+/**
+ * Base class for implementing {@link CmsEditable}, mostly managing
+ * {@link CmsEditionListener}s.
+ */
public abstract class AbstractCmsEditable implements CmsEditable {
private IdentityHashMap<CmsEditionListener, Object> listeners = new IdentityHashMap<>();
+ /** Notifies listeners of a {@link CmsEditionEvent}. */
protected void notifyListeners(CmsEditionEvent e) {
if (CmsEditionEvent.START_EDITING == e.getType()) {
for (CmsEditionListener listener : listeners.keySet())
public final static Float NO_IMAGE_RATIO = 1f;
protected Cms2DSize resizeTo(Cms2DSize orig, Cms2DSize constraints) {
- if (constraints.getWidth() != 0 && constraints.getHeight() != 0) {
+ if (constraints.width() != 0 && constraints.height() != 0) {
return constraints;
- } else if (constraints.getWidth() == 0 && constraints.getHeight() == 0) {
+ } else if (constraints.width() == 0 && constraints.height() == 0) {
return orig;
- } else if (constraints.getHeight() == 0) {// force width
- return new Cms2DSize(constraints.getWidth(),
- scale(orig.getHeight(), orig.getWidth(), constraints.getWidth()));
- } else if (constraints.getWidth() == 0) {// force height
- return new Cms2DSize(scale(orig.getWidth(), orig.getHeight(), constraints.getHeight()),
- constraints.getHeight());
+ } else if (constraints.height() == 0) {// force width
+ return new Cms2DSize(constraints.width(),
+ scale(orig.height(), orig.width(), constraints.width()));
+ } else if (constraints.width() == 0) {// force height
+ return new Cms2DSize(scale(orig.width(), orig.height(), constraints.height()),
+ constraints.height());
}
throw new IllegalArgumentException("Cannot resize " + orig + " to " + constraints);
}
/** @return null if not available */
@Override
public StringBuilder getImageTagBuilder(M node, Cms2DSize size) {
- return getImageTagBuilder(node, Integer.toString(size.getWidth()), Integer.toString(size.getHeight()));
+ return getImageTagBuilder(node, Integer.toString(size.width()), Integer.toString(size.height()));
}
/** @return null if not available */
}
public static String img(String src, Cms2DSize size) {
- return img(src, Integer.toString(size.getWidth()), Integer.toString(size.getHeight()));
+ return img(src, Integer.toString(size.width()), Integer.toString(size.height()));
}
}
--- /dev/null
+package org.argeo.cms.ux.acr;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.spi.ContentProvider;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.api.cms.ux.CmsEditable;
+import org.argeo.cms.acr.ContentUtils;
+import org.argeo.cms.ux.AbstractCmsEditable;
+
+/** {@link CmsEditable} semantics for a {@link Content}. */
+public class ContentCmsEditable extends AbstractCmsEditable {
+
+ private final boolean canEdit;
+ /** The path of this content, relative to its content provider. */
+ private final String relativePath;
+ private final ProvidedSession session;
+ private final ContentProvider provider;
+
+ public ContentCmsEditable(Content content) {
+ ProvidedContent providedContent = (ProvidedContent) content;
+ canEdit = providedContent.canEdit();
+ session = providedContent.getSession();
+ provider = providedContent.getProvider();
+ relativePath = ContentUtils.relativize(provider.getMountPath(), content.getPath());
+ }
+
+ @Override
+ public Boolean canEdit() {
+ return canEdit;
+ }
+
+ @Override
+ public Boolean isEditing() {
+ return provider.isOpenForEdit(session, relativePath);
+ }
+
+ @Override
+ public void startEditing() {
+ provider.openForEdit(session, relativePath);
+ }
+
+ @Override
+ public void stopEditing() {
+ provider.freeze(session, relativePath);
+ }
+
+}
import org.argeo.cms.ux.widgets.AbstractHierarchicalPart;
import org.argeo.cms.ux.widgets.HierarchicalPart;
+/** A {@link HierarchicalPart} based on {@link Content}. */
public class ContentHierarchicalPart extends AbstractHierarchicalPart<Content> implements HierarchicalPart<Content> {
@Override
- public List<Content> getChildren(Content content) {
+ public List<Content> getChildren(Content parent) {
List<Content> res = new ArrayList<>();
- if (isLeaf(content))
+ if (isLeaf(parent))
return res;
- if (content == null)
+ if (parent == null)
return res;
- for (Iterator<Content> it = content.iterator(); it.hasNext();) {
+ for (Iterator<Content> it = parent.iterator(); it.hasNext();) {
res.add(it.next());
}
public interface ContentPart {
Content getContent();
- @Deprecated
- default Content getNode() {
- return getContent();
- }
+// @Deprecated
+// default Content getNode() {
+// return getContent();
+// }
}
<provide interface="com.sun.net.httpserver.HttpHandler"/>
</service>
<property name="context.path" type="String" value="/api/acr/" />
+ <property name="context.public" type="String" value="true" />
<reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.spi.ProvidedRepository" name="ProvidedRepository" policy="static"/>
</scr:component>
import javax.xml.namespace.QName;
import org.argeo.api.acr.Content;
+import org.argeo.api.acr.CrAttributeType;
import org.argeo.api.acr.CrName;
import org.argeo.api.acr.NamespaceUtils;
import org.argeo.api.acr.spi.ProvidedContent;
}
/*
- * ATTRIBUTES OPERATIONS
+ * ATTRIBUTES MAP IMPLEMENTATION
*/
-// protected abstract Iterable<QName> keys();
-//
-// protected abstract void removeAttr(QName key);
-
@Override
public Set<Entry<QName, Object>> entrySet() {
Set<Entry<QName, Object>> result = new AttrSet();
return result;
}
+ @Override
+ public Object get(Object key) {
+ return get((QName) key, Object.class).orElse(null);
+ }
+
+ /*
+ * ATTRIBUTES OPERATIONS
+ */
@Override
public Class<?> getType(QName key) {
return String.class;
if (value == null)
return new ArrayList<>();
if (value instanceof List) {
- if (isDefaultAttrTypeRequested(clss))
+ if (clss.isAssignableFrom(Object.class))
return (List<A>) value;
List<A> res = new ArrayList<>();
List<?> lst = (List<?>) value;
for (Object o : lst) {
- A item = clss.isAssignableFrom(String.class) ? (A) o.toString() : (A) o;
+ A item = CrAttributeType.cast(clss, o).get();
res.add(item);
}
return res;
} else {// singleton
-// try {
- A res = (A) value;
+ A res = CrAttributeType.cast(clss, value).get();
return Collections.singletonList(res);
-// } catch (ClassCastException e) {
-// return Optional.empty();
-// }
}
}
return ancestors.size();
}
+ @Override
+ public boolean isRoot() {
+ return CrName.root.qName().equals(getName());
+ }
+
@Override
public String getSessionLocalId() {
return getPath();
/*
* UTILITIES
*/
- protected boolean isDefaultAttrTypeRequested(Class<?> clss) {
- // check whether clss is Object.class
- return clss.isAssignableFrom(Object.class);
- }
+// protected boolean isDefaultAttrTypeRequested(Class<?> clss) {
+// // check whether clss is Object.class
+// return clss.isAssignableFrom(Object.class);
+// }
// @Override
// public String toString() {
@Override
public <A> Optional<A> get(QName key, Class<A> clss) {
- return null;
+ return Optional.empty();
}
protected void removeAttr(QName key) {
--- /dev/null
+package org.argeo.cms.acr;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.ContentName;
+import org.argeo.api.acr.spi.ContentProvider;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.api.cms.CmsConstants;
+
+/**
+ * Base for simple content providers based on a service supporting only one
+ * namespace. Typically used in higher level applications for domain-specific
+ * modelling.
+ */
+public abstract class AbstractSimpleContentProvider<SERVICE> implements ContentProvider {
+ private final String namespaceUri;
+ private final String defaultPrefix;
+ private SERVICE service;
+ private String mountPath;
+ private String mountName;
+
+ protected AbstractSimpleContentProvider(String namespaceUri, String defaultPrefix) {
+ this(namespaceUri, defaultPrefix, null, null);
+ }
+
+ protected AbstractSimpleContentProvider(String namespaceUri, String defaultPrefix, SERVICE service,
+ String mountPath) {
+ this.namespaceUri = namespaceUri;
+ this.defaultPrefix = defaultPrefix;
+ this.service = service;
+ setMountPath(mountPath);
+ }
+
+ /** The first level of content provided by the service. */
+ protected abstract Iterator<Content> firstLevel(ProvidedSession session);
+
+ /**
+ * Retrieve the content at this relative path. Root content is already dealt
+ * with.
+ */
+ protected abstract ProvidedContent get(ProvidedSession session, List<String> segments);
+
+ @Override
+ public final ProvidedContent get(ProvidedSession session, String relativePath) {
+ List<String> segments = ContentUtils.toPathSegments(relativePath);
+ if (segments.size() == 0)
+ return new ServiceContent(session);
+ return get(session, segments);
+ }
+
+ public void start(Map<String, String> properties) {
+ mountPath = properties.get(CmsConstants.ACR_MOUNT_PATH);
+ if (mountPath == null)
+ throw new IllegalStateException(CmsConstants.ACR_MOUNT_PATH + " must be specified.");
+ setMountPath(mountPath);
+ }
+
+ private void setMountPath(String mountPath) {
+ if (mountPath == null)
+ return;
+ this.mountPath = mountPath;
+ List<String> mountSegments = ContentUtils.toPathSegments(mountPath);
+ this.mountName = mountSegments.get(mountSegments.size() - 1);
+
+ }
+
+ @Override
+ public String getNamespaceURI(String prefix) {
+ if (defaultPrefix.equals(prefix))
+ return namespaceUri;
+ throw new IllegalArgumentException("Only prefix " + defaultPrefix + " is supported");
+ }
+
+ @Override
+ public Iterator<String> getPrefixes(String namespaceURI) {
+ if (namespaceUri.equals(namespaceURI))
+ return Collections.singletonList(defaultPrefix).iterator();
+ throw new IllegalArgumentException("Only namespace URI " + namespaceUri + " is supported");
+ }
+
+ @Override
+ public String getMountPath() {
+ return mountPath;
+ }
+
+ protected String getMountName() {
+ return mountName;
+ }
+
+ protected SERVICE getService() {
+ return service;
+ }
+
+ public void setService(SERVICE service) {
+ this.service = service;
+ }
+
+ protected class ServiceContent extends AbstractContent {
+
+ public ServiceContent(ProvidedSession session) {
+ super(session);
+ }
+
+ @Override
+ public ContentProvider getProvider() {
+ return AbstractSimpleContentProvider.this;
+ }
+
+ @Override
+ public QName getName() {
+ return new ContentName(getMountName());
+ }
+
+ @Override
+ public Content getParent() {
+ return null;
+ }
+
+ @Override
+ public Iterator<Content> iterator() {
+ return firstLevel(getSession());
+ }
+
+ }
+
+}
CmsSession cmsSession = CurrentUser.getCmsSession();
CmsContentSession contentSession = userSessions.get(cmsSession);
if (contentSession == null) {
- final CmsContentSession newContentSession = new CmsContentSession(this, cmsSession.getUuid(),
+ final CmsContentSession newContentSession = new CmsContentSession(this, cmsSession.uuid(),
cmsSession.getSubject(), locale, uuidFactory);
cmsSession.addOnCloseCallback((c) -> {
newContentSession.close();
import org.argeo.api.acr.spi.ProvidedRepository;
import org.argeo.api.acr.spi.ProvidedSession;
import org.argeo.api.uuid.UuidFactory;
-import org.argeo.cms.acr.xml.DomContentProvider;
+import org.argeo.api.uuid.UuidIdentified;
+import org.argeo.cms.CurrentUser;
/** Implements {@link ProvidedSession}. */
-class CmsContentSession implements ProvidedSession {
+class CmsContentSession implements ProvidedSession, UuidIdentified {
final private AbstractContentRepository contentRepository;
private final UUID uuid;
@Override
public Content get(String path) {
- if (!path.startsWith(ContentUtils.ROOT_SLASH))
+ if (!path.startsWith(Content.ROOT_PATH))
throw new IllegalArgumentException(path + " is not an absolute path");
ContentProvider contentProvider = contentRepository.getMountManager().findContentProvider(path);
String mountPath = contentProvider.getMountPath();
- String relativePath = extractRelativePath(mountPath, path);
+ String relativePath = ContentUtils.relativize(mountPath, path);
ProvidedContent content = contentProvider.get(CmsContentSession.this, relativePath);
return content;
}
@Override
public boolean exists(String path) {
- if (!path.startsWith(ContentUtils.ROOT_SLASH))
+ if (!path.startsWith(Content.ROOT_PATH))
throw new IllegalArgumentException(path + " is not an absolute path");
ContentProvider contentProvider = contentRepository.getMountManager().findContentProvider(path);
String mountPath = contentProvider.getMountPath();
- String relativePath = extractRelativePath(mountPath, path);
+ String relativePath = ContentUtils.relativize(mountPath, path);
return contentProvider.exists(this, relativePath);
}
- private String extractRelativePath(String mountPath, String path) {
- String relativePath = path.substring(mountPath.length());
- if (relativePath.length() > 0 && relativePath.charAt(0) == '/')
- relativePath = relativePath.substring(1);
- return relativePath;
- }
-
@Override
public Subject getSubject() {
return subject;
synchronized (CmsContentSession.this) {
// TODO optimise
for (ContentProvider provider : modifiedProviders) {
- if (provider instanceof DomContentProvider) {
- ((DomContentProvider) provider).persist(s);
- }
+ provider.persist(s);
+// if (provider instanceof DomContentProvider) {
+// ((DomContentProvider) provider).persist(s);
+// }
}
modifiedProviders.clear();
return s;
}
@Override
- public UUID getUuid() {
+ public UUID uuid() {
return uuid;
}
return sessionRunDir;
}
+ /*
+ * OBJECT METHODS
+ */
+
+ @Override
+ public boolean equals(Object o) {
+ return UuidIdentified.equals(this, o);
+ }
+
+ @Override
+ public int hashCode() {
+ return UuidIdentified.hashCode(this);
+ }
+
+ @Override
+ public String toString() {
+ return "Content Session " + uuid + " (" + CurrentUser.getUsername(subject) + ")";
+ }
+
/*
* SEARCH
*/
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.StringJoiner;
import java.util.function.BiConsumer;
import org.argeo.api.acr.ContentSession;
import org.argeo.api.acr.DName;
import org.argeo.api.cms.CmsAuth;
+import org.argeo.api.cms.CmsSession;
import org.argeo.api.cms.directory.CmsDirectory;
import org.argeo.api.cms.directory.CmsUserManager;
import org.argeo.api.cms.directory.HierarchyUnit;
public static final char SLASH = '/';
public static final String SLASH_STRING = Character.toString(SLASH);
- public static final String ROOT_SLASH = "" + SLASH;
public static final String EMPTY = "";
/**
public static List<String> toPathSegments(String path) {
List<String> res = new ArrayList<>();
- if (EMPTY.equals(path) || ROOT_SLASH.equals(path))
+ if (EMPTY.equals(path) || Content.ROOT_PATH.equals(path))
return res;
collectPathSegments(path, res);
return res;
}
}
- public static ContentSession openDataAdminSession(ContentRepository repository) {
+ public static ContentSession openDataAdminSession(ContentRepository contentRepository) {
LoginContext loginContext;
try {
loginContext = CmsAuth.DATA_ADMIN.newLoginContext();
ClassLoader currentCl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(ContentUtils.class.getClassLoader());
- return CurrentSubject.callAs(loginContext.getSubject(), () -> repository.get());
+ return CurrentSubject.callAs(loginContext.getSubject(), () -> contentRepository.get());
} finally {
Thread.currentThread().setContextClassLoader(currentCl);
}
}
+ public static ContentSession openSession(ContentRepository contentRepository, CmsSession cmsSession) {
+ return CurrentSubject.callAs(cmsSession.getSubject(), () -> contentRepository.get());
+ }
+
+ /**
+ * Constructs a relative path between a base path and a given path.
+ *
+ * @throws IllegalArgumentException if the base path is not an ancestor of the
+ * path
+ */
+ public static String relativize(String basePath, String path) throws IllegalArgumentException {
+ Objects.requireNonNull(basePath);
+ Objects.requireNonNull(path);
+ if (!path.startsWith(basePath))
+ throw new IllegalArgumentException(basePath + " is not an ancestor of " + path);
+ String relativePath = path.substring(basePath.length());
+ if (relativePath.length() > 0 && relativePath.charAt(0) == '/')
+ relativePath = relativePath.substring(1);
+ return relativePath;
+ }
+
/** Singleton. */
private ContentUtils() {
import org.argeo.api.acr.QNamed;
+/**
+ * Some core SVG attributes, which are used to standardise generic concepts such
+ * as width, height, etc.
+ */
public enum SvgAttrs implements QNamed {
/** */
width,
package org.argeo.cms.acr.directory;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Iterator;
import java.util.List;
-import javax.xml.namespace.QName;
-
import org.argeo.api.acr.ArgeoNamespace;
import org.argeo.api.acr.Content;
-import org.argeo.api.acr.ContentName;
import org.argeo.api.acr.ContentNotFoundException;
import org.argeo.api.acr.spi.ContentProvider;
import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.cms.directory.CmsUserManager;
import org.argeo.api.cms.directory.HierarchyUnit;
import org.argeo.api.cms.directory.UserDirectory;
-import org.argeo.cms.acr.AbstractContent;
+import org.argeo.cms.acr.AbstractSimpleContentProvider;
import org.argeo.cms.acr.ContentUtils;
import org.osgi.service.useradmin.User;
-public class DirectoryContentProvider implements ContentProvider {
- private String mountPath;
- private String mountName;
+/** A {@link ContentProvider} based on a {@link CmsUserManager} service. */
+public class DirectoryContentProvider extends AbstractSimpleContentProvider<CmsUserManager> {
- private CmsUserManager userManager;
+ public DirectoryContentProvider(CmsUserManager service, String mountPath) {
+ super(ArgeoNamespace.LDAP_NAMESPACE_URI, ArgeoNamespace.LDAP_DEFAULT_PREFIX, service, mountPath);
+ }
- public DirectoryContentProvider(String mountPath, CmsUserManager userManager) {
- this.mountPath = mountPath;
- List<String> mountSegments = ContentUtils.toPathSegments(mountPath);
- this.mountName = mountSegments.get(mountSegments.size() - 1);
- this.userManager = userManager;
+ @Override
+ protected Iterator<Content> firstLevel(ProvidedSession session) {
+ List<Content> res = new ArrayList<>();
+ for (UserDirectory userDirectory : getService().getUserDirectories()) {
+ DirectoryContent content = new DirectoryContent(session, DirectoryContentProvider.this, userDirectory);
+ res.add(content);
+ }
+ return res.iterator();
}
@Override
- public ProvidedContent get(ProvidedSession session, String relativePath) {
- List<String> segments = ContentUtils.toPathSegments(relativePath);
- if (segments.size() == 0)
- return new UserManagerContent(session);
+ public ProvidedContent get(ProvidedSession session, List<String> segments) {
String userDirectoryName = segments.get(0);
UserDirectory userDirectory = null;
- userDirectories: for (UserDirectory ud : userManager.getUserDirectories()) {
+ userDirectories: for (UserDirectory ud : getService().getUserDirectories()) {
if (userDirectoryName.equals(ud.getName())) {
userDirectory = ud;
break userDirectories;
}
}
if (userDirectory == null)
- throw new ContentNotFoundException(session, mountPath + "/" + relativePath,
+ throw new ContentNotFoundException(session, getMountPath() + "/" + ContentUtils.toPath(segments),
"Cannot find user directory " + userDirectoryName);
if (segments.size() == 1) {
return new DirectoryContent(session, this, userDirectory);
HierarchyUnit hierarchyUnit = userDirectory.getHierarchyUnit(pathWithinUserDirectory);
if (hierarchyUnit == null)
throw new ContentNotFoundException(session,
- mountPath + "/" + relativePath + "/" + pathWithinUserDirectory,
+ getMountPath() + "/" + ContentUtils.toPath(segments) + "/" + pathWithinUserDirectory,
"Cannot find " + pathWithinUserDirectory + " within " + userDirectoryName);
return new HierarchyUnitContent(session, this, hierarchyUnit);
}
@Override
public boolean exists(ProvidedSession session, String relativePath) {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
- public String getMountPath() {
- return mountPath;
- }
-
- @Override
- public String getNamespaceURI(String prefix) {
- if (ArgeoNamespace.LDAP_DEFAULT_PREFIX.equals(prefix))
- return ArgeoNamespace.LDAP_NAMESPACE_URI;
- throw new IllegalArgumentException("Only prefix " + ArgeoNamespace.LDAP_DEFAULT_PREFIX + " is supported");
- }
-
- @Override
- public Iterator<String> getPrefixes(String namespaceURI) {
- if (ArgeoNamespace.LDAP_NAMESPACE_URI.equals(namespaceURI))
- return Collections.singletonList(ArgeoNamespace.LDAP_DEFAULT_PREFIX).iterator();
- throw new IllegalArgumentException("Only namespace URI " + ArgeoNamespace.LDAP_NAMESPACE_URI + " is supported");
- }
-
- public void setUserManager(CmsUserManager userManager) {
- this.userManager = userManager;
+ // TODO optimise?
+ return exists(session, relativePath);
}
- public CmsUserManager getUserManager() {
- return userManager;
- }
+// public void setUserManager(CmsUserManager userManager) {
+// this.userManager = userManager;
+// }
- UserManagerContent getRootContent(ProvidedSession session) {
- return new UserManagerContent(session);
+ CmsUserManager getUserManager() {
+ return getService();
}
- /*
- * COMMON UTILITIES
- */
- class UserManagerContent extends AbstractContent {
-
- public UserManagerContent(ProvidedSession session) {
- super(session);
- }
-
- @Override
- public ContentProvider getProvider() {
- return DirectoryContentProvider.this;
- }
-
- @Override
- public QName getName() {
- return new ContentName(mountName);
- }
-
- @Override
- public Content getParent() {
- return null;
- }
-
- @Override
- public Iterator<Content> iterator() {
- List<Content> res = new ArrayList<>();
- for (UserDirectory userDirectory : userManager.getUserDirectories()) {
- DirectoryContent content = new DirectoryContent(getSession(), DirectoryContentProvider.this,
- userDirectory);
- res.add(content);
- }
- return res.iterator();
- }
-
+ ServiceContent getRootContent(ProvidedSession session) {
+ return new ServiceContent(session);
}
}
import org.argeo.api.acr.spi.ContentProvider;
import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.acr.spi.ProvidedSession;
-import org.argeo.api.cms.CmsLog;
import org.argeo.cms.acr.AbstractContent;
import org.argeo.cms.acr.ContentUtils;
import org.argeo.cms.util.FsUtils;
/** Content persisted as a filesystem {@link Path}. */
public class FsContent extends AbstractContent implements ProvidedContent {
- private CmsLog log = CmsLog.getLog(FsContent.class);
+// private CmsLog log = CmsLog.getLog(FsContent.class);
final static String USER_ = "user:";
// TODO check file names with ':' ?
if (isMountBase) {
String mountPath = provider.getMountPath();
- if (mountPath != null && !mountPath.equals(ContentUtils.ROOT_SLASH)) {
+ if (mountPath != null && !mountPath.equals(Content.ROOT_PATH)) {
Content mountPoint = session.getMountPoint(mountPath);
this.name = mountPoint.getName();
} else {
String[] arr = str.split("\n");
if (arr.length == 1) {
- if (clss.isAssignableFrom(String.class)) {
- res = (A) arr[0];
- } else {
- res = (A) CrAttributeType.parse(arr[0]);
- }
+// if (clss.isAssignableFrom(String.class)) {
+// res = (A) arr[0];
+// } else {
+// res = (A) CrAttributeType.parse(arr[0]);
+// }
+// if (isDefaultAttrTypeRequested(clss))
+// return Optional.of((A) CrAttributeType.parse(str));
+ return CrAttributeType.cast(clss, str);
+
} else {
List<Object> lst = new ArrayList<>();
for (String s : arr) {
}
}
if (res == null) {
- if (isDefaultAttrTypeRequested(clss))
- return Optional.of((A) CrAttributeType.parse(value.toString()));
- if (clss.isAssignableFrom(value.getClass()))
- return Optional.of((A) value);
- if (clss.isAssignableFrom(String.class))
- return Optional.of((A) value.toString());
- log.warn("Cannot interpret " + key + " in " + this);
- return Optional.empty();
+// if (isDefaultAttrTypeRequested(clss))
+// return Optional.of((A) CrAttributeType.parse(value.toString()));
+ return CrAttributeType.cast(clss, value);
+// if (clss.isAssignableFrom(value.getClass()))
+// return Optional.of((A) value);
+// if (clss.isAssignableFrom(String.class))
+// return Optional.of((A) value.toString());
+// log.warn("Cannot interpret " + key + " in " + this);
+// return Optional.empty();
// try {
// res = (A) value;
// } catch (ClassCastException e) {
@Override
public List<QName> getContentClasses() {
- List<QName> res = new ArrayList<>();
- List<String> value = getMultiple(DName.resourcetype.qName(), String.class);
- for (String s : value) {
- QName name = NamespaceUtils.parsePrefixedName(provider, s);
- res.add(name);
- }
+// List<QName> res = new ArrayList<>();
+// List<String> value = getMultiple(DName.resourcetype.qName(), String.class);
+// for (String s : value) {
+// QName name = NamespaceUtils.parsePrefixedName(provider, s);
+// res.add(name);
+// }
+ List<QName> res = getMultiple(DName.resourcetype.qName(), QName.class);
if (Files.isDirectory(path))
res.add(DName.collection.qName());
return res;
package org.argeo.cms.acr.xml;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.argeo.api.acr.Content;
import org.argeo.api.acr.ContentName;
+import org.argeo.api.acr.CrAttributeType;
import org.argeo.api.acr.CrName;
import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.acr.spi.ProvidedSession;
if (isLocalRoot()) {// root
String mountPath = provider.getMountPath();
if (mountPath != null) {
- if (ContentUtils.ROOT_SLASH.equals(mountPath)) {
+ if (Content.ROOT_PATH.equals(mountPath)) {
return CrName.root.qName();
}
Content mountPoint = getSession().getMountPoint(mountPath);
return result;
}
- @SuppressWarnings("unchecked")
+// @SuppressWarnings("unchecked")
@Override
public <A> Optional<A> get(QName key, Class<A> clss) {
String namespaceUriOrNull = XMLConstants.NULL_NS_URI.equals(key.getNamespaceURI()) ? null
: key.getNamespaceURI();
if (element.hasAttributeNS(namespaceUriOrNull, key.getLocalPart())) {
String value = element.getAttributeNS(namespaceUriOrNull, key.getLocalPart());
- if (clss.isAssignableFrom(String.class))
- return Optional.of((A) value);
- else
- return Optional.empty();
+// if (isDefaultAttrTypeRequested(clss))
+// return Optional.of((A) CrAttributeType.parse(value));
+ return CrAttributeType.cast(clss, value);
} else
return Optional.empty();
}
String mountPath = provider.getMountPath();
if (mountPath == null)
return null;
- if (ContentUtils.ROOT_SLASH.equals(mountPath)) {
+ if (Content.ROOT_PATH.equals(mountPath)) {
return null;
}
String[] parent = ContentUtils.getParentPath(mountPath);
List<QName> res = new ArrayList<>();
if (isLocalRoot()) {
String mountPath = provider.getMountPath();
- if (mountPath != null) {
+ if (Content.ROOT_PATH.equals(mountPath)) {// repository root
+ res.add(CrName.root.qName());
+ } else {
Content mountPoint = getSession().getMountPoint(mountPath);
res.addAll(mountPoint.getContentClasses());
}
public void addContentClasses(QName... contentClass) {
if (isLocalRoot()) {
String mountPath = provider.getMountPath();
- if (mountPath != null) {
+ if (Content.ROOT_PATH.equals(mountPath)) {// repository root
+ throw new IllegalArgumentException("Cannot add content classes to repository root");
+ } else {
Content mountPoint = getSession().getMountPoint(mountPath);
mountPoint.addContentClasses(contentClass);
}
};
}
-// @Override
-// public Content get() {
-// return new DomContent(this, document.getDocumentElement());
-// }
-
-// public Element createElement(String name) {
-// return document.createElementNS(null, name);
-//
-// }
-
@Override
public ProvidedContent get(ProvidedSession session, String relativePath) {
if ("".equals(relativePath))
if (relativePath.startsWith("/"))
throw new IllegalArgumentException("Relative path cannot start with /");
String xPathExpression = '/' + relativePath;
- if ("/".equals(mountPath))
+ if (Content.ROOT_PATH.equals(mountPath)) // repository root
xPathExpression = "/" + CrName.root.qName() + xPathExpression;
try {
NodeList nodes = (NodeList) xPath.get().evaluate(xPathExpression, document, XPathConstants.NODESET);
return nodes.getLength() != 0;
}
+ @Override
public void persist(ProvidedSession session) {
if (mountPath != null) {
Content mountPoint = session.getMountPoint(mountPath);
public final static <T> T doAs(Supplier<T> supplier, RemoteAuthRequest req) {
CmsSession cmsSession = getCmsSession(req);
return CurrentSubject.callAs(cmsSession.getSubject(), () -> supplier.get());
-// ClassLoader currentContextCl = Thread.currentThread().getContextClassLoader();
-// Thread.currentThread().setContextClassLoader(RemoteAuthUtils.class.getClassLoader());
-// try {
-// return Subject.doAs(
-// Subject.getSubject((AccessControlContext) req.getAttribute(AccessControlContext.class.getName())),
-// new PrivilegedAction<T>() {
-//
-// @Override
-// public T run() {
-// return supplier.get();
-// }
-//
-// });
-// } finally {
-// Thread.currentThread().setContextClassLoader(currentContextCl);
-// }
}
-// public final static void configureRequestSecurity(RemoteAuthRequest req) {
-// if (req.getAttribute(AccessControlContext.class.getName()) != null)
-// throw new IllegalStateException("Request already authenticated.");
-// AccessControlContext acc = AccessController.getContext();
-// req.setAttribute(REMOTE_USER, CurrentUser.getUsername());
-// req.setAttribute(AccessControlContext.class.getName(), acc);
-// }
-//
-// public final static void clearRequestSecurity(RemoteAuthRequest req) {
-// if (req.getAttribute(AccessControlContext.class.getName()) == null)
-// throw new IllegalStateException("Cannot clear non-authenticated request.");
-// req.setAttribute(REMOTE_USER, null);
-// req.setAttribute(AccessControlContext.class.getName(), null);
-// }
-
public static CmsSession getCmsSession(RemoteAuthRequest req) {
CmsSession cmsSession = (CmsSession) req.getAttribute(CmsSession.class.getName());
if (cmsSession == null)
import javax.xml.namespace.NamespaceContext;
import org.argeo.api.acr.ContentNotFoundException;
+import org.argeo.api.cms.CmsLog;
import org.argeo.cms.http.HttpHeader;
import org.argeo.cms.http.HttpMethod;
import org.argeo.cms.http.HttpStatus;
* ACR-specific code more readable and maintainable.
*/
public abstract class DavHttpHandler implements HttpHandler {
+ private final static CmsLog log = CmsLog.getLog(DavHttpHandler.class);
@Override
public void handle(HttpExchange exchange) throws IOException {
exchange.sendResponseHeaders(HttpStatus.NOT_FOUND.getCode(), -1);
}
// TODO return a structured error message
+ // TODO better filter application errors and failed login etc.
catch (UnsupportedOperationException e) {
e.printStackTrace();
exchange.sendResponseHeaders(HttpStatus.NOT_IMPLEMENTED.getCode(), -1);
} catch (Exception e) {
+ log.error("Failed HTTP exchange " + exchange.getRequestURI(), e);
exchange.sendResponseHeaders(HttpStatus.INTERNAL_SERVER_ERROR.getCode(), -1);
}
import org.argeo.api.cms.CmsConstants;
import org.argeo.api.cms.CmsLog;
import org.argeo.api.cms.CmsSession;
+import org.argeo.api.uuid.UuidIdentified;
import org.argeo.cms.internal.runtime.CmsContextImpl;
import org.osgi.service.useradmin.Authorization;
/** Default CMS session implementation. */
-public class CmsSessionImpl implements CmsSession, Serializable {
+public class CmsSessionImpl implements CmsSession, Serializable, UuidIdentified {
private static final long serialVersionUID = 1867719354246307225L;
private final static CmsLog log = CmsLog.getLog(CmsSessionImpl.class);
}
@Override
- public UUID getUuid() {
+ public UUID uuid() {
return uuid;
}
views.put(uid, view);
}
+ /*
+ * OBJECT METHODS
+ */
+
+ @Override
+ public boolean equals(Object o) {
+ return UuidIdentified.equals(this, o);
+ }
+
+ @Override
+ public int hashCode() {
+ return UuidIdentified.hashCode(this);
+ }
+
+ @Override
public String toString() {
return "CMS Session " + userDn + " localId=" + localSessionId + ", uuid=" + uuid;
}
try {
super.start();
// FIXME does not work on Windows
- //Path rootXml = KernelUtils.getOsgiInstancePath(ROOT_XML);
+ // Path rootXml = KernelUtils.getOsgiInstancePath(ROOT_XML);
initRootContentProvider(null);
// Path srvPath = KernelUtils.getOsgiInstancePath(CmsConstants.SRV_WORKSPACE);
}
// users
- DirectoryContentProvider directoryContentProvider = new DirectoryContentProvider(
- CmsContentRepository.DIRECTORY_BASE, userManager);
+ DirectoryContentProvider directoryContentProvider = new DirectoryContentProvider(userManager,
+ CmsContentRepository.DIRECTORY_BASE);
addProvider(directoryContentProvider);
// remote
minor=1
micro=112
qualifier=.next
+
+Bundle-Copyright= \
+Copyright 2007-2023 Mathieu Baudier, \
+Copyright 2012-2023 Argeo GmbH
+
+SPDX-License-Identifier= \
+LGPL-2.1-or-later \
+OR EPL-2.0 \
+OR LicenseRef-argeo2-GPL-2.0-or-later-with-EPL-and-JCR-permissions
major=2
minor=3
-micro=17
+micro=18
qualifier=
Bundle-Copyright= \
package org.argeo.cms.swt;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
import org.argeo.api.cms.ux.Cms2DSize;
import org.argeo.cms.ux.AbstractImageManager;
-import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
/** Manages only public images so far. */
public abstract class AbstractSwtImageManager<M> extends AbstractImageManager<Control, M> {
- protected abstract Image getSwtImage(M node);
+ protected abstract ImageData getSwtImageData(M node);
protected abstract String noImg(Cms2DSize size);
- public Boolean load(M node, Control control, Cms2DSize preferredSize) {
+ @Override
+ public Boolean load(M node, Control control, Cms2DSize preferredSize, URI link) {
Cms2DSize imageSize = getImageSize(node);
Cms2DSize size;
String imgTag = null;
- if (preferredSize == null || imageSize.getWidth() == 0 || imageSize.getHeight() == 0
- || (preferredSize.getWidth() == 0 && preferredSize.getHeight() == 0)) {
- if (imageSize.getWidth() != 0 && imageSize.getHeight() != 0) {
+ if (preferredSize == null || imageSize.width() == 0 || imageSize.height() == 0
+ || (preferredSize.width() == 0 && preferredSize.height() == 0)) {
+ if (imageSize.width() != 0 && imageSize.height() != 0) {
// actual image size if completely known
size = imageSize;
} else {
imgTag = noImg(size);
}
- } else if (preferredSize.getWidth() != 0 && preferredSize.getHeight() != 0) {
+ } else if (preferredSize.width() != 0 && preferredSize.height() != 0) {
// given size if completely provided
size = preferredSize;
} else {
// at this stage :
// image is completely known
- assert imageSize.getWidth() != 0 && imageSize.getHeight() != 0;
+ assert imageSize.width() != 0 && imageSize.height() != 0;
// one and only one of the dimension as been specified
- assert preferredSize.getWidth() == 0 || preferredSize.getHeight() == 0;
+ assert preferredSize.width() == 0 || preferredSize.height() == 0;
size = resizeTo(imageSize, preferredSize);
}
}
Label lbl = (Label) control;
+ StringBuilder sb = new StringBuilder();
+ if (link != null)
+ sb.append("<a href='").append(URLEncoder.encode(link.toString(), StandardCharsets.UTF_8)).append("'>");
+ sb.append(imgTag);
+ if (link != null)
+ sb.append("</a>");
+
lbl.setText(imgTag);
// lbl.setSize(size);
// } else if (control instanceof FileUpload) {
public Cms2DSize getImageSize(M node) {
// TODO optimise
- Image image = getSwtImage(node);
- return new Cms2DSize(image.getBounds().width, image.getBounds().height);
+ ImageData imageData = getSwtImageData(node);
+ return new Cms2DSize(imageData.width, imageData.height);
}
}
import javax.security.auth.Subject;
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.ContentSession;
-import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.cms.CmsLog;
import org.argeo.api.cms.ux.CmsEditable;
import org.argeo.cms.CurrentUser;
throw new IllegalStateException("Edited should not be null or disposed at this stage");
}
- /** Persist all changes. */
- protected void persistChanges(ContentSession session) {
-// session.save();
-// session.refresh(false);
- // TODO notify that changes have been persisted
- }
-
- /** Convenience method using a Node in order to save the underlying session. */
- protected void persistChanges(Content anyNode) {
- persistChanges(((ProvidedContent) anyNode).getSession());
- }
+// /** Persist all changes. */
+// protected void persistChanges(ContentSession session) {
+//// session.save();
+//// session.refresh(false);
+// // TODO notify that changes have been persisted
+// }
+//
+// /** Convenience method using a Node in order to save the underlying session. */
+// protected void persistChanges(Content anyNode) {
+// persistChanges(((ProvidedContent) anyNode).getSession());
+// }
/** Notify edition exception */
protected void notifyEditionException(Throwable e) {
package org.argeo.cms.swt.acr;
+import java.io.IOException;
import java.io.InputStream;
+import java.util.Optional;
import org.argeo.api.acr.Content;
import org.argeo.api.cms.CmsConstants;
import org.argeo.api.cms.ux.Cms2DSize;
+import org.argeo.cms.acr.SvgAttrs;
import org.argeo.cms.swt.AbstractSwtImageManager;
import org.argeo.cms.swt.CmsSwtUtils;
import org.argeo.cms.ux.CmsUxUtils;
-import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+/** Implementation of {@link AbstractSwtImageManager} based on ACR. */
public class AcrSwtImageManager extends AbstractSwtImageManager<Content> {
@Override
}
@Override
- protected Image getSwtImage(Content node) {
- throw new UnsupportedOperationException();
+ protected ImageData getSwtImageData(Content node) {
+ try (InputStream in = node.open(InputStream.class)) {
+ ImageData imageData = new ImageData(in);
+ return imageData;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
buf.append(node.getPath());
return buf.toString();
}
+
+ @Override
+ public Cms2DSize getImageSize(Content node) {
+ // TODO cache it?
+ Optional<Integer> width = node.get(SvgAttrs.width, Integer.class);
+ Optional<Integer> height = node.get(SvgAttrs.height, Integer.class);
+ if (!width.isEmpty() && !height.isEmpty())
+ return new Cms2DSize(width.get(), height.get());
+ return super.getImageSize(node);
+ }
+
}
import org.argeo.api.acr.Content;
import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.cms.ux.acr.ContentPart;
import org.eclipse.swt.widgets.Composite;
/** A composite which can (optionally) manage a content. */
-public class ContentComposite extends Composite {
+public class ContentComposite extends Composite implements ContentPart {
private static final long serialVersionUID = -1447009015451153367L;
public ContentComposite(Composite parent, int style, Content item) {
return getData() instanceof Content;
}
+ @Override
public Content getContent() {
return (Content) getData();
}
--- /dev/null
+package org.argeo.cms.swt.acr;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.swt.widgets.StyledControl;
+import org.argeo.cms.ux.acr.ContentPart;
+import org.eclipse.swt.widgets.Composite;
+
+public abstract class ContentStyledControl extends StyledControl implements ContentPart {
+
+ private static final long serialVersionUID = -5714246408818696583L;
+
+ public ContentStyledControl(Composite parent, int swtStyle, Content content) {
+ super(parent, swtStyle);
+ setData(content);
+ }
+
+ @Override
+ public Content getContent() {
+ return (Content) getData();
+ }
+
+}
import org.argeo.api.cms.ux.Cms2DSize;
import org.argeo.api.cms.ux.CmsImageManager;
import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.widgets.EditableImage;
import org.argeo.cms.ux.acr.ContentPart;
import org.argeo.eclipse.ui.specific.CmsFileUpload;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
/** An image within the Argeo Text framework */
-public class Img extends EditableImage implements SwtSectionPart, ContentPart {
+public class Img extends LinkedControl implements SwtSectionPart, ContentPart {
private static final long serialVersionUID = 6233572783968188476L;
private final SwtSection section;
private final CmsImageManager<Control, Content> imageManager;
-// private FileUploadHandler currentUploadHandler = null;
-// private FileUploadListener fileUploadListener;
+
+ private Cms2DSize preferredImageSize;
public Img(Composite parent, int swtStyle, Content imgNode, Cms2DSize preferredImageSize) {
this(SwtSection.findSection(parent), parent, swtStyle, imgNode, preferredImageSize, null);
-// setStyle(TextStyles.TEXT_IMAGE);
}
public Img(Composite parent, int swtStyle, Content imgNode) {
this(SwtSection.findSection(parent), parent, swtStyle, imgNode, null, null);
-// setStyle(TextStyles.TEXT_IMAGE);
}
public Img(Composite parent, int swtStyle, Content imgNode, CmsImageManager<Control, Content> imageManager) {
this(SwtSection.findSection(parent), parent, swtStyle, imgNode, null, imageManager);
-// setStyle(TextStyles.TEXT_IMAGE);
}
Img(SwtSection section, Composite parent, int swtStyle, Content imgNode, Cms2DSize preferredImageSize,
CmsImageManager<Control, Content> imageManager) {
- super(parent, swtStyle, preferredImageSize);
+ super(parent, swtStyle);
+ this.preferredImageSize = preferredImageSize;
this.section = section;
- this.imageManager = imageManager != null ? imageManager
- : (CmsImageManager<Control, Content>) CmsSwtUtils.getCmsView(section).getImageManager();
-// CmsSwtUtils.style(this, TextStyles.TEXT_IMG);
+ this.imageManager = imageManager != null ? imageManager : CmsSwtUtils.getCmsView(section).getImageManager();
setData(imgNode);
}
}
}
- @Override
- public synchronized void stopEditing() {
- super.stopEditing();
-// fileUploadListener = null;
- }
-
- @Override
protected synchronized Boolean load(Control lbl) {
Content imgNode = getContent();
- boolean loaded = imageManager.load(imgNode, lbl, getPreferredImageSize());
- // getParent().layout();
+ boolean loaded = imageManager.load(imgNode, lbl, preferredImageSize, toUri());
return loaded;
}
+ protected Label createLabel(Composite box, String style) {
+ Label lbl = new Label(box, getStyle());
+ // lbl.setLayoutData(CmsUiUtils.fillWidth());
+ CmsSwtUtils.markup(lbl);
+ CmsSwtUtils.style(lbl, style);
+ if (mouseListener != null)
+ lbl.addMouseListener(mouseListener);
+ load(lbl);
+ return lbl;
+ }
+
protected Content getUploadFolder() {
return getContent().getParent();
}
return "Img #" + getPartId();
}
+ public void setPreferredSize(Cms2DSize size) {
+ this.preferredImageSize = size;
+ }
+
+ public Cms2DSize getPreferredImageSize() {
+ return preferredImageSize;
+ }
+
+ public void setPreferredImageSize(Cms2DSize preferredImageSize) {
+ this.preferredImageSize = preferredImageSize;
+ }
}
--- /dev/null
+package org.argeo.cms.swt.acr;
+
+import java.net.URI;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.widgets.StyledControl;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * A {@link StyledControl} which can link either to an internal {@link Content}
+ * or an external URI.
+ */
+public abstract class LinkedControl extends StyledControl {
+
+ private static final long serialVersionUID = -7603153425459801216L;
+
+ private Content linkedContent;
+ private URI plainUri;
+
+ public LinkedControl(Composite parent, int swtStyle) {
+ super(parent, swtStyle);
+ }
+
+ public void setLink(Content linkedContent) {
+ if (plainUri != null)
+ throw new IllegalStateException("An URI is already set");
+ this.linkedContent = linkedContent;
+ }
+
+ public void setLink(URI uri) {
+ if (linkedContent != null)
+ throw new IllegalStateException("A linked content is already set");
+ this.plainUri = uri;
+ }
+
+ public boolean isInternalLink() {
+ if (!hasLink())
+ throw new IllegalStateException("No link has been set");
+ return linkedContent != null;
+ }
+
+ public boolean hasLink() {
+ return plainUri != null || linkedContent != null;
+ }
+
+ public Content getLinkedContent() {
+ return linkedContent;
+ }
+
+ public URI getPlainUri() {
+ return plainUri;
+ }
+
+ public URI toUri() {
+ if (plainUri != null)
+ return plainUri;
+ if (linkedContent != null)
+ return URI.create("#" + CmsSwtUtils.cleanPathForUrl(linkedContent.getPath()));
+ return null;
+
+ }
+
+}
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
public class BundleSvgTheme extends BundleCmsSwtTheme {
private final static Logger logger = System.getLogger(BundleSvgTheme.class.getName());
- private Map<String, Map<Integer, Image>> imageCache = Collections.synchronizedMap(new HashMap<>());
+ private Map<String, Map<Integer, ImageData>> imageDataCache = Collections.synchronizedMap(new HashMap<>());
private Map<Integer, ImageTranscoder> transcoders = Collections.synchronizedMap(new HashMap<>());
+ private final static String IMAGE_CACHE_KEY = BundleSvgTheme.class.getName() + ".imageCache";
+
@Override
public Image getIcon(String name, Integer preferredSize) {
String path = "icons/types/svg/" + name + ".svg";
return createImageFromSvg(path, preferredSize);
}
+ @SuppressWarnings("unchecked")
protected Image createImageFromSvg(String path, Integer preferredSize) {
+ Display display = Display.getCurrent();
+ Objects.requireNonNull(display, "Not a user interface thread");
+
+ Map<String, Map<Integer, Image>> imageCache = (Map<String, Map<Integer, Image>>) display
+ .getData(IMAGE_CACHE_KEY);
+ if (imageCache == null)
+ display.setData(IMAGE_CACHE_KEY, new HashMap<String, Map<Integer, Image>>());
+ imageCache = (Map<String, Map<Integer, Image>>) display.getData(IMAGE_CACHE_KEY);
+
Image image = null;
if (imageCache.containsKey(path)) {
image = imageCache.get(path).get(preferredSize);
if (image != null)
return image;
ImageData imageData = loadFromSvg(path, preferredSize);
- image = new Image(Display.getDefault(), imageData);
+ image = new Image(display, imageData);
if (!imageCache.containsKey(path))
imageCache.put(path, Collections.synchronizedMap(new HashMap<>()));
imageCache.get(path).put(preferredSize, image);
}
protected ImageData loadFromSvg(String path, int size) {
+ ImageData imageData = null;
+ if (imageDataCache.containsKey(path))
+ imageData = imageDataCache.get(path).get(size);
+ if (imageData != null)
+ return imageData;
+
ImageTranscoder transcoder = null;
synchronized (this) {
transcoder = transcoders.get(size);
transcoders.put(size, transcoder);
}
}
- ImageData imageData;
try (InputStream in = getResourceAsStream(path); ByteArrayOutputStream out = new ByteArrayOutputStream();) {
if (in == null)
throw new IllegalArgumentException(path + " not found");
throw new RuntimeException("Cannot transcode SVG " + path, e);
}
+ // cache it
+ if (!imageDataCache.containsKey(path))
+ imageDataCache.put(path, Collections.synchronizedMap(new HashMap<>()));
+ imageDataCache.get(path).put(size, imageData);
+
return imageData;
}
// }
}
- @Override
- public void destroy(BundleContext bundleContext, Map<String, String> properties) {
- Display display = Display.getDefault();
- if (display != null)
- for (String path : imageCache.keySet()) {
- for (Image image : imageCache.get(path).values()) {
- display.syncExec(() -> image.dispose());
- }
- }
- super.destroy(bundleContext, properties);
- }
+// @Override
+// public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+// Display display = Display.getDefault();
+// if (display != null)
+// for (String path : imageCache.keySet()) {
+// for (Image image : imageCache.get(path).values()) {
+// display.syncExec(() -> image.dispose());
+// }
+// }
+// super.destroy(bundleContext, properties);
+// }
}
--- /dev/null
+package org.argeo.cms.swt.widgets;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsStyle;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.service.ResourceManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.osgi.framework.BundleContext;
+
+/** A link to an internal or external location. */
+public class CmsLink {
+ private final static CmsLog log = CmsLog.getLog(CmsLink.class);
+ private BundleContext bundleContext;
+
+ private String label;
+ private String style;
+ private String target;
+ private String image;
+ private boolean openNew = false;
+ private MouseListener mouseListener;
+
+ private int horizontalAlignment = SWT.CENTER;
+ private int verticalAlignment = SWT.CENTER;
+
+ // internal
+ // private Boolean isUrl = false;
+ private Integer imageWidth, imageHeight;
+
+ public CmsLink() {
+ super();
+ }
+
+ public CmsLink(String label, String target) {
+ this(label, target, (String) null);
+ }
+
+ public CmsLink(String label, String target, CmsStyle style) {
+ this(label, target, style != null ? style.style() : null);
+ }
+
+ public CmsLink(String label, String target, String style) {
+ super();
+ this.label = label;
+ this.target = target;
+ this.style = style;
+ init();
+ }
+
+ public void init() {
+ if (image != null) {
+ ImageData image = loadImage();
+ if (imageHeight == null && imageWidth == null) {
+ imageWidth = image.width;
+ imageHeight = image.height;
+ } else if (imageHeight == null) {
+ imageHeight = (imageWidth * image.height) / image.width;
+ } else if (imageWidth == null) {
+ imageWidth = (imageHeight * image.width) / image.height;
+ }
+ }
+ }
+
+ /** @return {@link Composite} with a single {@link Label} child. */
+ public Control createUi(Composite parent) {
+// if (image != null && (imageWidth == null || imageHeight == null)) {
+// throw new CmsException("Image is not properly configured."
+// + " Make sure bundleContext property is set and init() method has been called.");
+// }
+
+ Composite comp = new Composite(parent, SWT.NONE);
+ comp.setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+ Label link = new Label(comp, SWT.NONE);
+ CmsSwtUtils.markup(link);
+ GridData layoutData = new GridData(horizontalAlignment, verticalAlignment, false, false);
+ if (image != null) {
+ if (imageHeight != null)
+ layoutData.heightHint = imageHeight;
+ if (label == null)
+ if (imageWidth != null)
+ layoutData.widthHint = imageWidth;
+ }
+
+ link.setLayoutData(layoutData);
+ CmsSwtUtils.style(comp, style != null ? style : getDefaultStyle());
+ CmsSwtUtils.style(link, style != null ? style : getDefaultStyle());
+
+ // label
+ StringBuilder labelText = new StringBuilder();
+ if (target != null) {
+ labelText.append("<a style='color:inherit;text-decoration:inherit;' href='");
+ labelText.append(target).append("'");
+ if (openNew) {
+ labelText.append(" target='_blank'");
+ }
+ labelText.append(">");
+ }
+ if (image != null) {
+ registerImageIfNeeded();
+ String imageLocation = RWT.getResourceManager().getLocation(image);
+ labelText.append("<img");
+ if (imageWidth != null)
+ labelText.append(" width='").append(imageWidth).append('\'');
+ if (imageHeight != null)
+ labelText.append(" height='").append(imageHeight).append('\'');
+ labelText.append(" src=\"").append(imageLocation).append("\"/>");
+
+ }
+
+ if (label != null) {
+ labelText.append(' ').append(label);
+ }
+
+ if (target != null)
+ labelText.append("</a>");
+
+ link.setText(labelText.toString());
+
+ if (mouseListener != null)
+ link.addMouseListener(mouseListener);
+
+ return comp;
+ }
+
+ private void registerImageIfNeeded() {
+ ResourceManager resourceManager = RWT.getResourceManager();
+ if (!resourceManager.isRegistered(image)) {
+ URL res = getImageUrl();
+ try (InputStream inputStream = res.openStream()) {
+ resourceManager.register(image, inputStream);
+ if (log.isTraceEnabled())
+ log.trace("Registered image " + image);
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot load image " + image, e);
+ }
+ }
+ }
+
+ private ImageData loadImage() {
+ URL url = getImageUrl();
+ ImageData result = null;
+ try (InputStream inputStream = url.openStream()) {
+ result = new ImageData(inputStream);
+ if (log.isTraceEnabled())
+ log.trace("Loaded image " + image);
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot load image " + image, e);
+ }
+ return result;
+ }
+
+ private URL getImageUrl() {
+ URL url;
+ try {
+ // pure URL
+ url = new URL(image);
+ } catch (MalformedURLException e1) {
+ url = bundleContext.getBundle().getResource(image);
+ }
+
+ if (url == null)
+ throw new IllegalStateException("No image " + image + " available.");
+
+ return url;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public void setStyle(String style) {
+ this.style = style;
+ }
+
+ /** @deprecated Use {@link #setStyle(String)} instead. */
+ @Deprecated
+ public void setCustom(String custom) {
+ this.style = custom;
+ }
+
+ public void setTarget(String target) {
+ this.target = target;
+ }
+
+ public void setImage(String image) {
+ this.image = image;
+ }
+
+ public void setMouseListener(MouseListener mouseListener) {
+ this.mouseListener = mouseListener;
+ }
+
+ public void setvAlign(String vAlign) {
+ if ("bottom".equals(vAlign)) {
+ verticalAlignment = SWT.BOTTOM;
+ } else if ("top".equals(vAlign)) {
+ verticalAlignment = SWT.TOP;
+ } else if ("center".equals(vAlign)) {
+ verticalAlignment = SWT.CENTER;
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported vertical alignment " + vAlign + " (must be: top, bottom or center)");
+ }
+ }
+
+ public void setImageWidth(Integer imageWidth) {
+ this.imageWidth = imageWidth;
+ }
+
+ public void setImageHeight(Integer imageHeight) {
+ this.imageHeight = imageHeight;
+ }
+
+ public void setOpenNew(boolean openNew) {
+ this.openNew = openNew;
+ }
+
+ protected String getDefaultStyle() {
+ return "link";
+// return SimpleStyle.link.name();
+ }
+}
import org.eclipse.swt.widgets.Text;
/** A stylable and editable image. */
+@Deprecated
public abstract class EditableImage extends StyledControl {
private static final long serialVersionUID = -5689145523114022890L;
private final static CmsLog log = CmsLog.getLog(EditableImage.class);
loaded = true;
if (control != null) {
((Label) control).setText(imgTag);
- control.setSize(preferredImageSize != null
- ? new Point(preferredImageSize.getWidth(), preferredImageSize.getHeight())
- : getSize());
+ control.setSize(
+ preferredImageSize != null ? new Point(preferredImageSize.width(), preferredImageSize.height())
+ : getSize());
} else {
loaded = false;
}
private boolean useTextAsLabel = false;
+ /**
+ * Message to display if there is not value. Only used with SWT.FLAT (label
+ * displayed with a {@link Text})
+ *
+ * @see Text#setMessage(String)
+ */
+ private String message;
+
public EditableText(Composite parent, int style) {
super(parent, style);
editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY));
}
protected Text createTextLabel(Composite box, String style) {
- Text lbl = new Text(box, getStyle() | (multiLine ? SWT.MULTI : SWT.SINGLE));
+ Text lbl = new Text(box, getStyle() | (multiLine ? SWT.MULTI | SWT.WRAP : SWT.SINGLE));
lbl.setEditable(false);
- lbl.setLayoutData(CmsSwtUtils.fillWidth());
+ if (message != null)
+ lbl.setMessage(message);
+ lbl.setLayoutData(multiLine ? CmsSwtUtils.fillAll() : CmsSwtUtils.fillWidth());
if (style != null)
CmsSwtUtils.style(lbl, style);
CmsSwtUtils.markup(lbl);
protected Text createText(Composite box, String style, boolean editable) {
highlight = new Composite(box, SWT.NONE);
highlight.setBackground(highlightColor);
- GridData highlightGd = new GridData(SWT.FILL, SWT.FILL, false, false);
+ GridData highlightGd = new GridData(SWT.FILL, SWT.FILL, false, multiLine);
highlightGd.widthHint = 5;
- highlightGd.heightHint = 3;
+ if (!multiLine)
+ highlightGd.heightHint = 3;
highlight.setLayoutData(highlightGd);
final Text text = new Text(box, getStyle() | (multiLine ? SWT.MULTI : SWT.SINGLE) | SWT.WRAP);
text.setEditable(editable);
- GridData textLayoutData = CmsSwtUtils.fillWidth();
+ GridData textLayoutData = multiLine ? CmsSwtUtils.fillAll() : CmsSwtUtils.fillWidth();
// textLayoutData.heightHint = preferredHeight;
text.setLayoutData(textLayoutData);
if (style != null)
this.useTextAsLabel = useTextAsLabel;
}
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ Control control = getControl();
+ if (control != null && control instanceof Text txt)
+ txt.setMessage(this.message);
+ }
+
+ @Override
+ protected void setContainerLayoutData(Composite composite) {
+ if (multiLine)
+ composite.setLayoutData(CmsSwtUtils.fillAll());
+ else
+ super.setContainerLayoutData(composite);
+ }
+
+ @Override
+ protected void setControlLayoutData(Control control) {
+// if (multiLine)
+// control.setLayoutData(CmsSwtUtils.fillAll());
+// else
+ super.setControlLayoutData(control);
+ }
+
}
setStyle(style.style());
}
+ /**
+ * Set the style, creating all related controls and composites. It should be
+ * called <b>after</b> all properties have been set.
+ */
public void setStyle(String style) {
Object currentStyle = null;
if (control != null)
}
}
+ /**
+ * Convenience method when no style is explicitly set, so that the control can
+ * effectively be created. Does nothing if a control already exists, otherwise
+ * it is equivalent to {@link #setStyle(String)} with a <code>null<code>
+ * argument.
+ */
+ public void initControl() {
+ if (control != null)
+ return;
+ setStyle((String) null);
+ }
+
/** To be overridden */
protected void setControlLayoutData(Control control) {
control.setLayoutData(CmsSwtUtils.fillWidth());
return null;
}
});
- } catch (Throwable e) {
- if (e instanceof SWTError) {
- SWTError swtError = (SWTError) e;
- if (swtError.code == SWT.ERROR_FUNCTION_DISPOSED) {
- log.error("Unexpected SWT error in event loop, ignoring it. " + e.getMessage());
- continue eventLoop;
- } else {
- log.error("Unexpected SWT error in event loop, shutting down...", e);
- break eventLoop;
- }
- } else if (e instanceof ThreadDeath) {
- throw (ThreadDeath) e;
- } else if (e instanceof Error) {
- log.error("Unexpected error in event loop, shutting down...", e);
- break eventLoop;
- } else {
- log.error("Unexpected exception in event loop, ignoring it. " + e.getMessage());
+ } catch (SWTError e) {
+ SWTError swtError = (SWTError) e;
+ if (swtError.code == SWT.ERROR_FUNCTION_DISPOSED) {
+ log.error("Unexpected SWT error in event loop, ignoring it. " + e.getMessage());
continue eventLoop;
+ } else {
+ log.error("Unexpected SWT error in event loop, shutting down...", e);
+ break eventLoop;
}
+ } catch (ThreadDeath e) {
+ // ThreadDeath is expected when the UI thread terminates
+ throw (ThreadDeath) e;
+ } catch (Error e) {
+ log.error("Unexpected error in event loop, shutting down...", e);
+ break eventLoop;
+ } catch (Throwable e) {
+ log.error("Unexpected exception in event loop, ignoring it. " + e.getMessage());
+ continue eventLoop;
}
}
if (!display.isDisposed())
+++ /dev/null
-package org.argeo.eclipse.ui.specific;
-
-import org.eclipse.swt.widgets.FileDialog;
-import org.eclipse.swt.widgets.Shell;
-
-public class CmsFileDialog extends FileDialog {
- private static final long serialVersionUID = -7540791204102318801L;
-
- public CmsFileDialog(Shell parent, int style) {
- super(parent, style);
- }
-
- public CmsFileDialog(Shell parent) {
- super(parent);
- }
-
-}
Import-Package:\
org.argeo.cms.auth,\
org.eclipse.swt,\
+org.eclipse.swt.widgets,\
org.eclipse.swt.graphics,\
org.w3c.css.sac,\
org.freedesktop.dbus.connections,\