/*
* 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.ivyde.common.model;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public abstract class IvyFile {
private static final Pattern ATTRIBUTE_NAME_PATTERN = Pattern
.compile("[^\"]*\"[\\s]*=[\\s]*([\\w\\-]+)");
private static final Pattern QUALIFIER_PATTERN = Pattern.compile("[\\w\\-<]*");
private static final Pattern ATTRIBUTE_VALUE_PATTERN = Pattern
.compile("([a-zA-Z0-9]+)[ ]*=[ ]*\"([^\"]*)\"");
private String doc;
private int currentOffset;
private String reversed;
private String projectName;
private IvyModelSettings settings;
public IvyFile(IvyModelSettings settings, String projectName, String doc) {
this(settings, projectName, doc, 0);
}
public IvyFile(IvyModelSettings settings, String projectName, String doc, int currentOffset) {
this.settings = settings;
this.projectName = projectName;
this.doc = doc;
this.reversed = new StringBuffer(doc).reverse().toString();
this.currentOffset = currentOffset;
}
protected String getDoc() {
return doc;
}
protected int getCurrentOffset() {
return currentOffset;
}
protected String getReversedDoc() {
return reversed;
}
public boolean inTag() {
return inTag(currentOffset);
}
public boolean inTag(int documentOffset) {
int lastSpaceIndex = documentOffset;
boolean hasSpace = false;
while (true) {
// Read character backwards
if (documentOffset == 0) {
return false;
}
char c = doc.charAt(--documentOffset);
if (Character.isWhitespace(c)) {
hasSpace = true;
}
if (c == '>' && (documentOffset == 0 || doc.charAt(documentOffset - 1) != '-')) {
return false;
}
if (c == '<'
&& (documentOffset + 1 >= doc.length() || (doc.charAt(documentOffset + 1) != '!' && doc
.charAt(documentOffset + 1) != '?'))) {
return hasSpace;
}
}
}
public String getTagName() {
return getTagName(currentOffset);
}
/**
* Return the tag for the position. Note : the documentoffset is considered to be in a tag ie in
* < >
*
* @param documentOffset
* @return
*/
public String getTagName(int documentOffset) {
int offset = documentOffset;
int lastSpaceIndex = offset;
while (true) {
// Read character backwards
char c = doc.charAt(--offset);
if (Character.isWhitespace(c)) {
lastSpaceIndex = offset;
continue;
}
if (c == '<') {
return doc.substring(offset + 1, lastSpaceIndex).trim();
}
}
}
public boolean readyForValue() {
return readyForValue(currentOffset);
}
public boolean readyForValue(int documentOffset) {
return getAttributeName(documentOffset) != null;
}
public int getStringIndexBackward(String string) {
return getStringIndexBackward(string, currentOffset);
}
public int getStringIndexBackward(String string, int documentOffset) {
String text = doc.substring(0, documentOffset);
return text.lastIndexOf(string);
}
public int getStringIndexForward(String string) {
return getStringIndexForward(string, currentOffset);
}
public int getStringIndexForward(String string, int documentOffset) {
return doc.indexOf(string, documentOffset);
}
public Map getAllAttsValues() {
return getAllAttsValues(currentOffset);
}
public Map getAllAttsValues(int documentOffset) {
Map result = new HashMap();
int offset = documentOffset;
int start = reversed.indexOf('<', getReverseOffset(documentOffset));
if (start != -1) {
start = getReverseOffset(start);
} else {
start = 0;
}
int end;
if (doc.charAt(documentOffset) == '>' && getAttributeName(documentOffset) == null) {
end = documentOffset + 1;
} else {
Pattern p = Pattern.compile("[^\\-]>");
Matcher m = p.matcher(doc);
if (m.find(documentOffset)) {
end = m.end();
} else {
end = doc.length();
}
}
Pattern regexp = ATTRIBUTE_VALUE_PATTERN;
try {
String tag = doc.substring(start, end);
tag = tag.substring(tag.indexOf(' '));
Matcher m = regexp.matcher(tag);
while (m.find()) {
String key = m.group(1);
String val = m.group(2);
result.put(key, val);
if (m.end() + m.group(0).length() < tag.length()) {
tag = tag.substring(m.end());
m = regexp.matcher(tag);
}
}
} catch (Exception e) {
// FIXME : what is really catched here ?
if (settings != null) {
settings.logError("Something bad happened", e);
}
}
return result;
}
public String getQualifier() {
return getQualifier(currentOffset);
}
/**
* Return the user typed string before calling completion stop on:<br>
* < to match tag,<br/>
* space to found attribute name<br/>
*
* @param documentOffset
* @return
*/
public String getQualifier(int documentOffset) {
Pattern p = QUALIFIER_PATTERN;
Matcher m = p.matcher(reversed);
if (m.find(getReverseOffset(documentOffset))) {
return doc.substring(getReverseOffset(m.end()), documentOffset);
} else {
return "";
}
}
public String getAttributeValueQualifier() {
return getAttributeValueQualifier(currentOffset);
}
/**
* Return the user typed string before calling completion on attribute value stop on:<br>
* " to match value for attribute
*
* @param documentOffset
* @return
*/
public String getAttributeValueQualifier(int documentOffset) {
int index = reversed.indexOf("\"", getReverseOffset(documentOffset));
if (index == -1) {
return "";
} else {
return doc.substring(getReverseOffset(index), documentOffset);
}
}
/**
* Returns the attribute name corresponding to the value currently edited
*
* @return null if current offset is not in an attibute value
*/
public String getAttributeName() {
return getAttributeName(currentOffset);
}
public String getAttributeName(int documentOffset) {
Pattern p = ATTRIBUTE_NAME_PATTERN;
Matcher m = p.matcher(reversed.substring(getReverseOffset(documentOffset)));
if (m.find() && m.start() == 0) {
String attName = new StringBuffer(m.group(1)).reverse().toString();
return attName;
} else {
return null;
}
}
public String getParentTagName() {
return getParentTagName(currentOffset);
}
public String getParentTagName(int documentOffset) {
int[] indexes = getParentTagIndex(documentOffset);
String foundParent = getString(indexes);
return foundParent == null ? null : foundParent.trim();
}
public String getString(int[] indexes) {
if (indexes != null) {
return doc.substring(indexes[0], indexes[1]);
} else {
return null;
}
}
public String getString(int start, int end) {
return doc.substring(start, end);
}
public int[] getParentTagIndex(int documentOffset) {
if (doc.length() <= documentOffset) {
return null;
}
int offset = documentOffset;
int lastSpaceIndex = offset;
int parentEndTagIndex = -1;
boolean parentEndTagReached = false;
boolean inSimpleTag = false;
Stack stack = new Stack();
while (offset > 0) {
char c = doc.charAt(--offset);
if (c == '>' && doc.charAt(offset - 1) != '-') {
if (doc.charAt(offset - 1) != '/') { // not a simple tag
// System.out.println("parentEndTagReached:"+doc.get(documentOffset-15,
// 15));
parentEndTagReached = true;
parentEndTagIndex = offset;
lastSpaceIndex = offset;
// System.out.println("parentEndTagReached:"+doc.get(documentOffset-15,
// 15));
continue;
} else { // simple tag
inSimpleTag = true;
}
} else if (c == '<') {
if (inSimpleTag) { // simple tag end
inSimpleTag = false;
} else if (doc.charAt(offset + 1) == '/') { // closing tag
if (parentEndTagReached) {
parentEndTagReached = false;
stack.push(doc.substring(offset + 2, parentEndTagIndex).trim());
lastSpaceIndex = offset + 2;
continue;
}
} else { // opening tag
if (doc.charAt(offset + 1) != '!' && doc.charAt(offset + 1) != '?') {
// not a doc tag or xml
if (!stack.isEmpty()) { // we found the closing tag before
String closedName = (String) stack.peek();
if (closedName.equalsIgnoreCase(doc.substring(offset + 1, offset + 1
+ closedName.length()))) {
stack.pop();
continue;
}
} else {
if (parentEndTagReached) {
return new int[] {offset + 1, lastSpaceIndex};
}
}
}
}
} else if (Character.isWhitespace(c)) {
lastSpaceIndex = offset;
continue;
}
}
return null;
}
private int getReverseOffset(int documentOffset) {
return doc.length() - documentOffset;
}
public int getOffset() {
return currentOffset;
}
public int[] getParentTagIndex() {
return getParentTagIndex(currentOffset);
}
public String getProjectName() {
return projectName;
}
}