blob: bbf2af6a0719fb6006ac7669383295da23dc3976 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.sling.fsprovider.internal.mapper;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.collections.Transformer;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.fsprovider.internal.ContentFileExtensions;
import org.apache.sling.fsprovider.internal.FsResourceMapper;
import org.apache.sling.fsprovider.internal.parser.ContentElement;
import org.apache.sling.fsprovider.internal.parser.ContentFileCache;
public final class ContentFileResourceMapper implements FsResourceMapper {
// providerRoot + "/" to be used for prefix matching of paths
private final String providerRootPrefix;
// The "root" file or folder in the file system
private final File providerFile;
private final ContentFileExtensions contentFileExtensions;
private final ContentFileCache contentFileCache;
public ContentFileResourceMapper(String providerRoot, File providerFile,
ContentFileExtensions contentFileExtensions, ContentFileCache contentFileCache) {
this.providerRootPrefix = providerRoot.concat("/");
this.providerFile = providerFile;
this.contentFileExtensions = contentFileExtensions;
this.contentFileCache = contentFileCache;
}
@Override
public Resource getResource(final ResourceResolver resolver, final String resourcePath) {
if (contentFileExtensions.isEmpty()) {
return null;
}
ContentFile contentFile = getFile(resourcePath, null);
if (contentFile != null && contentFile.hasContent()) {
return new ContentFileResource(resolver, contentFile);
}
else {
return null;
}
}
@SuppressWarnings("unchecked")
@Override
public Iterator<Resource> getChildren(final ResourceResolver resolver, final Resource parent) {
if (contentFileExtensions.isEmpty()) {
return null;
}
final String parentPath = parent.getPath();
ContentFile parentContentFile = parent.adaptTo(ContentFile.class);
// not a FsResource, try to create one from the resource
if (parentContentFile == null) {
parentContentFile = getFile(parentPath, null);
if (parentContentFile == null) {
// check if parent is a file resource that contains a file content resource
File parentFile = parent.adaptTo(File.class);
if (parentFile != null && parentFile.isDirectory()) {
List<Resource> childResources = new ArrayList<>();
for (File file : parentFile.listFiles()) {
String filenameSuffix = contentFileExtensions.getSuffix(file);
if (filenameSuffix != null && !isNodeDescriptor(file)) {
String path = parentPath + "/" + StringUtils.substringBeforeLast(file.getName(), filenameSuffix);
ContentFile contentFile = new ContentFile(file, path, null, contentFileCache);
childResources.add(new ContentFileResource(resolver, contentFile));
}
}
if (!childResources.isEmpty()) {
return childResources.iterator();
}
}
// no children here
return null;
}
}
// get child resources from content fragments in content file
List<ContentFile> children = new ArrayList<>();
if (parentContentFile.hasContent()) {
Iterator<Map.Entry<String,ContentElement>> childMaps = parentContentFile.getChildren();
while (childMaps.hasNext()) {
Map.Entry<String,ContentElement> entry = childMaps.next();
children.add(parentContentFile.navigateToRelative(entry.getKey()));
}
}
if (children.isEmpty()) {
return null;
}
else {
return IteratorUtils.transformedIterator(children.iterator(), new Transformer() {
@Override
public Object transform(Object input) {
ContentFile contentFile = (ContentFile)input;
return new ContentFileResource(resolver, contentFile);
}
});
}
}
private ContentFile getFile(String path, String subPath) {
if (!StringUtils.startsWith(path, providerRootPrefix)) {
return null;
}
String relPath = Escape.resourceToFileName(path.substring(providerRootPrefix.length()));
for (String filenameSuffix : contentFileExtensions.getSuffixes()) {
File file = new File(providerFile, relPath + filenameSuffix);
if (file.exists()) {
return new ContentFile(file, path, subPath, contentFileCache);
}
}
// try to find in parent path which contains content fragment
String parentPath = ResourceUtil.getParent(path);
String nextSubPath = path.substring(parentPath.length() + 1)
+ (subPath != null ? "/" + subPath : "");
return getFile(parentPath, nextSubPath);
}
private boolean isNodeDescriptor(File file) {
for (String filenameSuffix : contentFileExtensions.getSuffixes()) {
if (StringUtils.endsWith(file.getPath(), filenameSuffix)) {
File fileWithoutSuffix = new File(StringUtils.substringBeforeLast(file.getPath(), filenameSuffix));
return fileWithoutSuffix.exists();
}
}
return false;
}
}