]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java
Introduce simple SWT app
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / acr / ContentUtils.java
1 package org.argeo.cms.acr;
2
3 import java.io.PrintStream;
4 import java.net.URLEncoder;
5 import java.nio.charset.StandardCharsets;
6 import java.util.ArrayList;
7 import java.util.List;
8 import java.util.Objects;
9 import java.util.StringJoiner;
10 import java.util.StringTokenizer;
11 import java.util.function.BiConsumer;
12
13 import javax.security.auth.login.LoginContext;
14 import javax.security.auth.login.LoginException;
15 import javax.xml.namespace.QName;
16
17 import org.argeo.api.acr.Content;
18 import org.argeo.api.acr.ContentRepository;
19 import org.argeo.api.acr.ContentSession;
20 import org.argeo.api.acr.DName;
21 import org.argeo.api.cms.CmsAuth;
22 import org.argeo.api.cms.CmsConstants;
23 import org.argeo.api.cms.CmsSession;
24 import org.argeo.api.cms.directory.CmsDirectory;
25 import org.argeo.api.cms.directory.CmsUserManager;
26 import org.argeo.api.cms.directory.HierarchyUnit;
27 import org.argeo.api.cms.directory.UserDirectory;
28 import org.argeo.cms.util.CurrentSubject;
29 import org.osgi.service.useradmin.Role;
30
31 /** Utilities and routines around {@link Content}. */
32 public class ContentUtils {
33 public static void traverse(Content content, BiConsumer<Content, Integer> doIt) {
34 traverse(content, doIt, (Integer) null);
35 }
36
37 public static void traverse(Content content, BiConsumer<Content, Integer> doIt, Integer maxDepth) {
38 doTraverse(content, doIt, 0, maxDepth);
39 }
40
41 private static void doTraverse(Content content, BiConsumer<Content, Integer> doIt, int currentDepth,
42 Integer maxDepth) {
43 doIt.accept(content, currentDepth);
44 if (maxDepth != null && currentDepth == maxDepth)
45 return;
46 int nextDepth = currentDepth + 1;
47 for (Content child : content) {
48 doTraverse(child, doIt, nextDepth, maxDepth);
49 }
50 }
51
52 public static void print(Content content, PrintStream out, int depth, boolean printText) {
53 StringBuilder sb = new StringBuilder();
54 for (int i = 0; i < depth; i++) {
55 sb.append(" ");
56 }
57 String prefix = sb.toString();
58 out.println(prefix + content.getName());
59 for (QName key : content.keySet()) {
60 out.println(prefix + " " + key + "=" + content.get(key));
61 }
62 if (printText) {
63 if (content.hasText()) {
64 out.println("<![CDATA[" + content.getText().trim() + "]]>");
65 }
66 }
67 }
68
69 // public static <T> boolean isString(T t) {
70 // return t instanceof String;
71 // }
72
73 public static final char SLASH = '/';
74 public static final String SLASH_STRING = Character.toString(SLASH);
75 public static final String EMPTY = "";
76
77 /**
78 * Split a path (with '/' separator) in an array of length 2, the first part
79 * being the parent path (which could be either absolute or relative), the
80 * second one being the last segment, (guaranteed to be without a '/').
81 */
82 public static String[] getParentPath(String path) {
83 if (path == null)
84 throw new IllegalArgumentException("Path cannot be null");
85 if (path.length() == 0)
86 throw new IllegalArgumentException("Path cannot be empty");
87 checkDoubleSlash(path);
88 int parentIndex = path.lastIndexOf(SLASH);
89 if (parentIndex == path.length() - 1) {// trailing '/'
90 path = path.substring(0, path.length() - 1);
91 parentIndex = path.lastIndexOf(SLASH);
92 }
93
94 if (parentIndex == -1) // no '/'
95 return new String[] { EMPTY, path };
96
97 return new String[] { parentIndex != 0 ? path.substring(0, parentIndex) : "" + SLASH,
98 path.substring(parentIndex + 1) };
99 }
100
101 public static String toPath(List<String> segments) {
102 // TODO checks
103 StringJoiner sj = new StringJoiner("/");
104 segments.forEach((s) -> sj.add(s));
105 return sj.toString();
106 }
107
108 public static List<String> toPathSegments(String path) {
109 List<String> res = new ArrayList<>();
110 if (EMPTY.equals(path) || Content.ROOT_PATH.equals(path))
111 return res;
112 collectPathSegments(path, res);
113 return res;
114 }
115
116 private static void collectPathSegments(String path, List<String> segments) {
117 String[] parent = getParentPath(path);
118 if (EMPTY.equals(parent[1])) // root
119 return;
120 segments.add(0, parent[1]);
121 if (EMPTY.equals(parent[0])) // end
122 return;
123 collectPathSegments(parent[0], segments);
124 }
125
126 public static void checkDoubleSlash(String path) {
127 if (path.contains(SLASH + "" + SLASH))
128 throw new IllegalArgumentException("Path " + path + " contains //");
129 }
130
131 /*
132 * DIRECTORY
133 */
134
135 public static Content roleToContent(CmsUserManager userManager, ContentSession contentSession, Role role) {
136 UserDirectory userDirectory = userManager.getDirectory(role);
137 String path = directoryPath(userDirectory) + userDirectory.getRolePath(role);
138 Content content = contentSession.get(path);
139 return content;
140 }
141
142 public static Content hierarchyUnitToContent(ContentSession contentSession, HierarchyUnit hierarchyUnit) {
143 CmsDirectory directory = hierarchyUnit.getDirectory();
144 StringJoiner relativePath = new StringJoiner(SLASH_STRING);
145 buildHierarchyUnitPath(hierarchyUnit, relativePath);
146 String path = directoryPath(directory) + relativePath.toString();
147 Content content = contentSession.get(path);
148 return content;
149 }
150
151 /** The path to this {@link CmsDirectory}. Ends with a /. */
152 private static String directoryPath(CmsDirectory directory) {
153 return CmsContentRepository.DIRECTORY_BASE + SLASH + directory.getName() + SLASH;
154 }
155
156 /** Recursively build a relative path of a {@link HierarchyUnit}. */
157 private static void buildHierarchyUnitPath(HierarchyUnit current, StringJoiner relativePath) {
158 if (current.getParent() == null) // directory
159 return;
160 buildHierarchyUnitPath(current.getParent(), relativePath);
161 relativePath.add(current.getHierarchyUnitName());
162 }
163
164 /*
165 * CONSUMER UTILS
166 */
167
168 public static Content createCollections(ContentSession session, String path) {
169 if (session.exists(path)) {
170 Content content = session.get(path);
171 if (!content.isContentClass(DName.collection.qName())) {
172 throw new IllegalStateException("Content " + path + " already exists, but is not a collection");
173 } else {
174 return content;
175 }
176 } else {
177 String[] parentPath = getParentPath(path);
178 Content parent = createCollections(session, parentPath[0]);
179 Content content = parent.add(parentPath[1], DName.collection.qName());
180 return content;
181 }
182 }
183
184 public static ContentSession openDataAdminSession(ContentRepository contentRepository) {
185 LoginContext loginContext;
186 try {
187 loginContext = CmsAuth.DATA_ADMIN.newLoginContext();
188 loginContext.login();
189 } catch (LoginException e1) {
190 throw new RuntimeException("Could not login as data admin", e1);
191 } finally {
192 }
193
194 ClassLoader currentCl = Thread.currentThread().getContextClassLoader();
195 try {
196 Thread.currentThread().setContextClassLoader(ContentUtils.class.getClassLoader());
197 return CurrentSubject.callAs(loginContext.getSubject(), () -> contentRepository.get());
198 } finally {
199 Thread.currentThread().setContextClassLoader(currentCl);
200 }
201 }
202
203 public static ContentSession openSession(ContentRepository contentRepository, CmsSession cmsSession) {
204 return CurrentSubject.callAs(cmsSession.getSubject(), () -> contentRepository.get());
205 }
206
207 /**
208 * Constructs a relative path between a base path and a given path.
209 *
210 * @throws IllegalArgumentException if the base path is not an ancestor of the
211 * path
212 */
213 public static String relativize(String basePath, String path) throws IllegalArgumentException {
214 Objects.requireNonNull(basePath);
215 Objects.requireNonNull(path);
216 if (!path.startsWith(basePath))
217 throw new IllegalArgumentException(basePath + " is not an ancestor of " + path);
218 String relativePath = path.substring(basePath.length());
219 if (relativePath.length() > 0 && relativePath.charAt(0) == '/')
220 relativePath = relativePath.substring(1);
221 return relativePath;
222 }
223
224 /** A path in the node repository */
225 public static String getDataPath(Content node) {
226 // TODO make it more configurable?
227 StringBuilder buf = new StringBuilder(CmsConstants.PATH_API_ACR);
228 buf.append(node.getPath());
229 return buf.toString();
230 }
231
232 /** A path in the node repository */
233 public static String getDataPathForUrl(Content node) {
234 return cleanPathForUrl(getDataPath(node));
235 }
236
237 /** Clean reserved URL characters for use in HTTP links. */
238 public static String cleanPathForUrl(String path) {
239 StringTokenizer st = new StringTokenizer(path, "/");
240 StringBuilder sb = new StringBuilder();
241 while (st.hasMoreElements()) {
242 sb.append('/');
243 String encoded = URLEncoder.encode(st.nextToken(), StandardCharsets.UTF_8);
244 encoded = encoded.replace("+", "%20");
245 sb.append(encoded);
246
247 }
248 return sb.toString();
249 }
250
251 /** Singleton. */
252 private ContentUtils() {
253
254 }
255
256 }